2015-09-16 10 views
7

Ho una functor che opera su un contenitore di tipo U di elementi di tipo T questo modoC++ determinare se un contenitore ha :: find()

template<typename T, template<typename...> class U> 
class asserter 
{ 
public: 
    asserter(U<T> &c) : container(c) { }; 
    void operator()(T lhs) 
    { 
     CU_ASSERT(container.find(lhs) != container.end()); 
    }; 
private: 
    U<T> &container; 
}; 

che posso usare come

std::set<std::string> a, c; 
... 
asserter<std::string, std::set> ass(c); 
for_each(a.begin(), a.end(), ass); 

Dove stiamo ignorando std::includes() per il momento.

Questo funziona perfettamente se il contenitore è uno dove è definito U::find(). Se non lo è, vorrei tornare a std::find(). D'altra parte preferisco usare U::find() su std::find() se è disponibile.

In C++ 11 (o 17 se necessario) è possibile determinare se è disponibile U::find() (possibilmente restrittivo per STL) per U e in tal caso utilizzarlo, altrimenti utilizzare std::find()?

risposta

7

SFINAE se l'espressione c.find(value) è ben formata. Il tipo di ritorno finale è C++ 11 e non è essenziale in ogni caso; semplifica la scrittura del tipo di reso: decltype(c.find(value)) anziché decltype(std::declval<Container&>().find(std::declval<const T&>())).

Se l'espressione sarebbe mal formata, il primo sovraccarico di find_impl viene rimosso dal set di sovraccarico, lasciando il secondo sovraccarico come l'unico valido. Il solito trucco int/long/0 per il terzo parametro rende il primo sovraccarico preferito quando entrambi sono validi.

template<class Container, class T> 
auto find_impl(Container& c, const T& value, int) -> decltype(c.find(value)){ 
    return c.find(value); 
} 

template<class Container, class T> 
auto find_impl(Container& c, const T& value, long) -> decltype(std::begin(c)){ 
    return std::find(std::begin(c), std::end(c), value); 
} 

template<class Container, class T> 
auto find(Container& c, const T& value) -> decltype(find_impl(c, value, 0)) { 
    return find_impl(c, value, 0); 
} 

Il disclaimer solito applica: questo si basa sull'espressione SFINAE, che attualmente non è supportato da MSVC; Microsoft prevede di aggiungere il supporto in un aggiornamento a MSVC 2015.

+4

Una risposta più utile spiegherebbe anche perché il codice funziona. Per esempio, mi aspettavo una risposta che impiegasse SFINAE. La parte 'decltype (...)' una tecnica SFINAE in C++ 17 (penso che questa sintassi di espressione lambda sia C++ 17)? Inoltre, 'int' usato per forzare quella firma ad avere la preferenza sul sovraccarico' long'? –

+0

Mi preoccupo che 'std :: find' sia per l'uguaglianza mentre' std :: set :: find' ecc è per equivalenza. Dobbiamo considerarlo? –

+0

@NickyC So che * teoricamente * sono diversi, ma quanto spesso accade nel mondo reale? Penso che 'set' e' map' (ei loro cugini 'multi') siano gli unici in cui è importante,' unordered_set' e 'unordered_map' dovrebbero usare l'uguaglianza. –