2015-05-14 15 views
6

Ho una funzione modello che desidero abilitare solo per contenitori standard (o contenitori compatibili con contenitori standard, che forniscono almeno una funzione membro begin()). Sto SFINAE-ing out non-container nel seguente modo:Come disattivare i parametri non contenitori di SFINAE

template<typename Container> 
typename Container::value_type 
f(const Container& c, 
    typename std::enable_if< 
     std::is_same< 
      decltype(*c.begin()), 
      typename Container::value_type 
     >::value 
    >::type* = nullptr) 
{ 
    // implementation here 
} 

Il std::is_same e decltype non guardare troppo elegante. C'è un modo migliore per farlo?

PS: bisogno di SFINAE qui perché ho un sovraccarico diverso

template<typename Derived> 
f(const Eigen::MatrixBase<Derived>& A) 

e ogni volta che provo f(some_Eigen_matrix), il sovraccarico Container finisce per essere raccolto, il compilatore sputa fuori un errore perché il tipo è carente begin().

+3

Perché stai facendo il controllo di uguaglianza, comunque? Non è sufficiente se l'espressione è valida? – Columbo

+0

'auto f (const Container & c) -> decltype (c.begin(), (void) c.end())' –

+0

Ok, è un po 'più disordinato. Il tipo restituito è una somma sugli elementi del contenitore. – vsoftco

risposta

6

Utilizzando void_t, possiamo solo fare un tipo di carattere per avere begin() e end() (e qualsiasi altra cosa si potrebbe desiderare di verificare la presenza di, come typename T::iterator, si può solo tenere accumulando espressioni):

template <typename T, typename = void> 
struct is_std_container : std::false_type { }; 

template <typename T> 
struct is_std_container<T, 
    void_t<decltype(std::declval<T&>().begin()), 
      decltype(std::declval<T&>().end()), 
      typename T::value_type 
      >> 
    : std::true_type { }; 

E poi basta SFINAE su quel:

template <typename Container> 
typename std::enable_if< 
    is_std_container<Container>::value, 
    typename Container::value_type 
>::type 
f(const Container& c) { .. } 

Inoltre, se si voleva davvero per verificare che begin() ti dà indietro un compa T::iterator (o almeno che sono l'uguaglianza rable), puoi farlo anche tu:

void_t< 
    decltype(begin(std::declval<T&>()) == std::declval<typename T::iterator>()) 
> 
+0

Sì, questo sembra davvero migliore, e probabilmente andrò da questa parte, dal momento che sto ri-usando questo controllo. Grazie! – vsoftco

+0

Il trucco 'void_t' sembra molto pulito, grazie per il collegamento al foglio. – vsoftco

+1

L'unica cosa che mi sfugge è perché il 'is_std_container ' prenderà la specializzazione 'true_type' quando' Container' è davvero un contenitore? Scusa se la domanda sembra sciocca. In realtà ho trovato http://stackoverflow.com/questions/27687389/how-does-void-t-work, lo leggerò. Penso che l'idea sia che una specializzazione parziale sia sempre migliore. INTELLIGENTE! – vsoftco

Problemi correlati