Abilita la funzione template se la class ha una funzione membro specifica

Ho scritto la seguente funzione template, che controlla se un container arbitary contiene un elemento specifico:

template<template class container_t, class item_t, class... rest_t> bool contains(const container_t &_container, const item_t &_item) { for(const item_t &otherItem : _container) { if(otherItem == _item) { return true; } } return false; } 

Questo funziona bene per la maggior parte dei contenitori. Tuttavia, per tutti i tipi di set (e mappe) non è ottimale poiché potremmo usare:

 template<template class set_t, class item_t, class... rest_t> bool contains(const set_t &_set, const item_t &_item) { return _set.count(_item) > 0; } 

Ovviamente non possiamo usare entrambi i modelli contemporaneamente a causa dell’ambiguità. Ora sto cercando un modo per usare std::enable_if per abilitare il primo modello se container_t non fornisce una funzione membro count e il secondo template se lo fa. Tuttavia non riesco a capire come controllare una funzione membro specif (usando C ++ 11).

Funzionalità C ++ 14, reimplementata:

 templatestruct voider{using type=void;}; templateusing void_t=typename voider::type; 

Una mini libreria di metaprogrammazione:

 templatestruct types{using type=types;}; namespace details { templateclass Z, class types, class=void> struct can_apply : std::false_type {}; templateclass Z, class...Ts> struct can_apply< Z, types, void_t< Z > >: std::true_type {}; } templateclass Z, class...Ts> using can_apply = details::can_apply>; 

can_apply< some_template, args... > eredita da true_type iff some_template è un’espressione valida (nel contesto immediato).

Ora per il tuo problema:

 template using dot_count_type = decltype( std::declval().count(std::declval()) ); template using has_dot_count = can_apply; 

e has_dot_count è una class di caratteri che eredita da true_type iff T.count(I) è un’espressione valida.

 namespace details { template bool contains(std::false_type, C const& c, I const& i) { for(auto&& x:c) { if(x == i) { return true; } } return false; } template bool contains(std::true_type, C const& c, I const& i) { return c.count(i) != 0; } } template bool contains( C const& c, I const& i ) { return details::contains( has_dot_count{}, c, i ); } 

che utilizza l’invio di tag al posto di SFINAE.

Usare find sembra un’idea migliore di .count come un lato. In effetti, in un caso dovresti usare. .find l’altro find . In entrambi i casi dovresti usare using std::end; auto e = end(c); using std::end; auto e = end(c); .

Per inciso, MSVC 2013 (non so del 2015) non gestisce questo tipo di SFINAE usato sopra. Lo chiamano “espressione SFINAE”. Hanno estensioni personalizzate per rilevare l’esistenza di una funzione membro. Ma questo è dovuto al fatto che sono lontani dallo standard C ++ 11.