2017-09-06 43 views
7

Farz edelim ki, aynı temel işlevselliği uygulayan, ancak farklı üye işlev adlarına sahip arabirimlere sahip birden çok sınıf için yeniden kullanmak istediğim bazı genel kodlar var. Örneğin, aşağıdaki sınıf, temel sınıfın erase üye işlevine sahipse, örn. std::set veya std::unordered_set.Derleme zamanında C++ 'da bir üye işlevi

template <typename T> 
static std::chrono::duration<double> set_insert_time(const typename T::value_type &v) { 
    T set; 
    std::chrono::time_point<std::chrono::high_resolution_clock> start, end; 
    start = std::chrono::high_resolution_clock::now(); 
    set.erase(v); 
    end = std::chrono::high_resolution_clock::now(); 
    return end - start; 
} 

Ancak, şimdi bu işlevin örn. Bunun yerine unsafe_erase adlı bir işlevi sağlayan tbb::concurrent_unordered_set.

Benim ilk yaklaşma aşağıdakileri tanımlayan ve bunun yerine set_ops<T>::erase(set, v) arayarak, kısmi şablon uzmanlık tip özelliklerini kullanmayı oldu. tbb::concurrent_unordered_set templated sınıf değil, bir tür olduğu için ne yazık ki bu derleme değil. Ayrıca anahtar türü için ikinci bir şablon bağımsız değişkenle tipi özelliği genişletmek için çalıştı, ama bu Tstd::mem_fn(&T<U>::erase) bir şablon olmadığı için derlenmeyecektir.

template <typename T> 
struct set_ops { 
    constexpr static auto erase = std::mem_fn(&T::erase); 
}; 

template <> 
struct set_ops<tbb::concurrent_unordered_set> { 
    constexpr static auto erase = std::mem_fn(&T::unsafe_erase); 
}; 

Ayrıca üye işlevini aşağıdaki gibi bir işlev şablonuyla sarmayı denedim. Bu derlenmiş gibi görünüyor, ancak tanımlanmamış referanslar nedeniyle bağlantı kuramıyorsa, ör. decltype ((({parm#1}.erase)({parm#2})),((bool)())) erase<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >(std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >&, std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >::key_type const&)

template <typename T> 
constexpr auto set_erase(T& s, const typename T::key_type &v) -> decltype(s.erase(v), bool()); 
template <typename T> 
constexpr auto set_erase(T& s, const typename T::key_type &v) -> decltype(s.unsafe_erase(v), bool()); 

nasıl derleme zamanında bu aliasing uygulanabilir gerekir? Her bir temel sınıf için soyut bir arabirimden miras alan bir uygulama sağlayabileceğimi ya da bir üye işlevine bir işaretçi kullanabileceğimi biliyorum, ancak herhangi bir çalışma zamanı yükünden kaçınmak istiyorum.

cevap

3

Sadece kısmi uzmanlaşma ile birlikte yardımcı yapılar basit sarıcı işlevleri sağlayabilmektedir: Bu Messing tüm engeller

template <typename T> 
static std::chrono::duration<double> set_insert_time(const typename T::value_type &v) { 
    T set; 
    std::chrono::time_point<std::chrono::high_resolution_clock> start, end; 
    start = std::chrono::high_resolution_clock::now(); 
    set_ops<T>::erase(set, v); 
    end = std::chrono::high_resolution_clock::now(); 
    return end - start; 
} 

:

template <typename T> 
struct set_ops { 
    static auto erase(T& t, const T::value_type& obj) { 
    return t.erase(obj); 
    } 
}; 

template <typename... T> 
struct set_ops<tbb::concurrent_unordered_set<T...>> { 
    using set_type = tbb::concurrent_unordered_set<T...>; 
    static auto erase(set_type& t, const typename set_type::value_type& obj) { 
    return t.unsafe_erase(obj); 
    } 
}; 

Sonra set_inert_time fonksiyonu böyle bir şey olmazdı üye işlev işaretçileriyle birlikte, ve her şeyi derleme zamanında güzel bir şekilde çözülebilir bırakır.

+1

gereksiz yere karmaşık görünüyor. "Silme" nin iki sürümü, aşırı yük çözünürlüğü tarafından seçilen ücretsiz işlevler olabilir. – MSalters

+0

@MSalters Ether kısmi uzmanlığı veya 'enable_if' SFINE hileleri. Aksi takdirde, "delete (tbb :: concurrent_unordered_set &, const tbb :: concurrent_unordered_set :: value_type &)' için belirsiz aşırı yüklenmelerle sonuçlanırsınız. –

0

Üye işlevleri üniformalı bir imzaya sahip olduğu sürece, üye işlevine, işaretçi olmayan bir şablon parametresi olarak veya derleme zamanı constexpr olarak kullanabilirsiniz, ancak sözdizimi ... bildiğiniz gibi Yine de C++.

Aşağıdaki kod gcc 7.1 için derler. Bunu test etmek için tbb kitaplığım yok, ancak diğer derleyiciler için çalışmalı.

// traits declaration 
template <typename T> struct set_ops; 

// this template use a non type template parameter, assuming the member 
// function's signature is like this: (pseudo code) 
// template <typename T> struct some_set_implementation<T> 
// { iterator_type erase(const value_type &); }; 
template <typename T, typename T::iterator_type (T::*memfn)(const typename T::value_type &) = set_ops<T>::erase> 
static std::chrono::duration<double> set_insert_time(const typename T::value_type &v) { 
    T set; 
    std::chrono::time_point<std::chrono::high_resolution_clock> start, end; 
    start = std::chrono::high_resolution_clock::now(); 
    (set.*memfn)(v); 
    end = std::chrono::high_resolution_clock::now(); 
    return end - start; 
} 

// this code use constexpr 
template <typename T> 
static std::chrono::duration<double> set_insert_time(const typename T::value_type &v) { 
    T set; 
    constexpr auto memfn = set_ops<T>::erase; 
    std::chrono::time_point<std::chrono::high_resolution_clock> start, end; 
    start = std::chrono::high_resolution_clock::now(); 
    (set.*memfn)(v); 
    end = std::chrono::high_resolution_clock::now(); 
    return end - start; 
} 

// here goes specilizations for the type trait 
template <typename T> 
struct set_ops<concurrent_unordered_set<T>> { 
    static constexpr auto erase = &concurrent_unordered_set<T>::unsafe_erase; 
}; 
template <typename T, template <typename> class CONTAINER> 
struct set_ops<CONTAINER<T>> { 
    static constexpr auto erase = &CONTAINER<T>::erase; 
}; 

DÜZENLEME:

üye işlev işaretçisi deliliğe unutun.

Miles tarafından cevaba bakınız. üye olmayan bir işlev sarıcı kesinlikle gitmek için temiz bir yoldur.

template <typename T> 
static std::chrono::duration<double> set_insert_time(const typename T::value_type &v) { 
    T set; 
    std::chrono::time_point<std::chrono::high_resolution_clock> start, end; 
    start = std::chrono::high_resolution_clock::now(); 
    if constexpr(requires{set.erase(v);}) set.erase(v); 
    else set.unsafe_erase(v); 
    end = std::chrono::high_resolution_clock::now(); 
    return end - start; 
} 

Ve şablon işlevi örneği önce kavramını denetleyerek iyi yapabileceğini: En derleyici Konsept TS uygularsa

1

, bu kadar basit olabilir.

+0

Bilmekte fayda var, ancak Konsept TS uygulamada kullanmak için biraz fazla yeni. – ddcc

+0

@ddcc Yeni şeyler tarafından çekildiğim, özellikle de imature bile olsa, eski sessizce buggy şablon kodunu çözerek kullanıyorum! Bir aracın kalitesi, çağında değil, sağladığı kaldıraçta kalıyor! İkinci olarak, bir yıl içinde bu cevap daha az muhtemel olacaktır. – Oliv

1

Sadece bazı SFINAE ile aşırı kullanmak olabilir:

template <typename F> 
static std::chrono::duration<double> timed_func(F&& f) { 
    std::chrono::time_point<std::chrono::high_resolution_clock> start, end; 
    start = std::chrono::high_resolution_clock::now(); 
    std::forward<F>(f)(); 
    end = std::chrono::high_resolution_clock::now(); 
    return end - start; 
} 


template <typename T> 
static auto set_insert_time(const typename T::value_type &v) 
-> decltype(
    static_cast<void>(std::declval<T&>().erase(v)), 
    std::declval<std::chrono::duration<double>>()) 
{ 
    T set; 
    return timed_func([&](){ set.erase(v); }); 
} 

template <typename T> 
static auto set_insert_time(const typename T::value_type &v) 
-> decltype(
    static_cast<void>(std::declval<T&>().unsafe_erase(v)), 
    std::declval<std::chrono::duration<double>>()) 
{ 
    T set; 
    return timed_func([&](){ set.unsafe_erase(v); }); 
} 
+0

Aynı zamanda, çağrı grafiği lambda işlevi nedeniyle tersine çevrilmiş olsa da, arayanın yerine "timed_func" ile aranan bir işleve sahiptir. – ddcc

İlgili konular