2017-01-19 1 views
3

Je veux fournir deux implémentations différentes d'un operator>> selon que le type donné est une sous-classe d'un type particulier:std :: is_base_of pour le paramètre de fonction vectorielle basé sur un modèle

class A {}; 

class B : public A{}; 
class C {}; 

template<typename T> 
std::istream& operator>>(std::istream& is, 
    std::vector<typename std::enable_if<std::is_base_of<A, T>::value>::type>& vec) 
{ 
    std::cout << "Special case called" << std::endl; 
    return is; 
} 

template<typename T> 
std::istream& operator>>(std::istream& is, 
    std::vector<T>& vec) 
{ 
    std::cout << "General case called" << std::endl; 
    return is; 
} 


void main(int argc, char **argv) 
{ 
    std::vector<A> a; 
    std::vector<B> b; 
    std::vector<C> c; 
    std::stringstream ss("A string"); 
    ss >> a; 
    ss >> b; 
    ss >> c; 
} 

qui imprime

General case called 
General case called 
General case called 

Modification de la deuxième définition de l'opérateur à

template<typename T> 
std::istream& operator>>(std::istream& is, 
    std::vector<typename std::enable_if<!std::is_base_of<A, T>::value>::type>& vec) 
{ 
    std::cout << "General case called" << std::endl; 
    return is; 
} 

Est-ce ne compilera pas à cause de

error C2678: binary '>>' : no operator found which takes a left-hand operand of type 'std::stringstream' 

Je me sers probablement std::enable_if mal. Mais qu'est-ce qui est correct? Y a-t-il des problèmes avec le modèle std::vector ici?

Répondre

8

Je ne pense pas que le std :: enable_if est dans la position la plus désirée ici, je le mettrais dans le type de retour pour permettre SFINAE:

template<typename T> 
typename std::enable_if<std::is_base_of<A, T>::value,std::istream>::type& 
operator>>(std::istream& is,std::vector<T>& vec) 
{ 
    std::cout << "Special case called" << std::endl; 
    return is; 
} 

template<typename T> 
typename std::enable_if<!std::is_base_of<A, T>::value,std::istream>::type& 
operator>>(std::istream& is,std::vector<T>& vec) 
{ 
    std::cout << "General case called" << std::endl; 
    return is; 
} 

live demo

+0

Merci beaucoup, cela résout le problème! J'ai une courte question complémentaire: Puis-je fournir une troisième spécialisation explicite?'' template <> std :: istream & opérateur >> (std :: istream & est, std :: vector & vec) {...} '' ne compile pas à cause de '' erreur C2794: 'type': n'est pas un membre de toute classe de base directe ou indirecte de 'std :: enable_if ' '' – PhilLab

+3

@PhilLab préfèrent la surcharge sur la fonction de spécialisation –

+0

duuhhh, j'ai raté l'évidence :-). si tout ce que vous avez est un marteau, tout ressemble à un clou – PhilLab

2

@ réponse de Biggy correctement démontre une façon de le faire correctement. Voici une alternative que je trouve un peu plus facile à lire (avec quelques corrections et améliorations mineures):

template<typename T, typename std::enable_if<std::is_base_of<A, T>{}, int>::type = 0> 
std::istream& operator >>(std::istream& is, std::vector<T>& vec) { 
    std::cout << "Special case called\n"; 
    return is; 
} 

template<typename T, typename std::enable_if<!std::is_base_of<A, T>{}, int>::type = 0> 
std::istream& operator >>(std::istream& is, std::vector<T>& vec) { 
    std::cout << "General case called\n"; 
    return is; 
} 

Online Demo

Ou mieux encore, en utilisant dispatching tag:

template<typename T> 
std::istream& impl(std::istream& is, std::vector<T>& vec, std::true_type) { 
    std::cout << "Special case called\n"; 
    return is; 
} 

template<typename T> 
std::istream& impl(std::istream& is, std::vector<T>& vec, std::false_type) { 
    std::cout << "General case called\n"; 
    return is; 
} 

template<typename T> 
std::istream& operator >>(std::istream& is, std::vector<T>& vec) { 
    return impl(is, vec, std::is_base_of<A, T>{}); 
} 

Online Demo

En pratique, j'ai trouvé que cette dernière approche était beaucoup plus facile à maintenir lorsque le nombre de conditions est élevé, et a pour résultat des messages d'erreur plus faciles à déchiffrer.

Pour ce qui est pourquoi votre code ne fonctionne pas, le problème est que std::enable_if<std::is_base_of<A, T>::value>::type comme type de paramètre n'est pas un contexte déduisent pour T, et il n'y a aucun autre paramètre impliquant T à déduction aide. Donc, parce que vous ne spécifiez pas explicitement T lorsque vous l'appelez, l'opérateur n'est tout simplement pas un candidat éligible à invoquer car T n'est pas connu. Lorsque vous mettez ensuite la même tentative-SFINAE dans l'autre surcharge, ni surcharge est viable et vous obtenez votre erreur. Une règle approximative est: si vous avez besoin de typename (comme avec l'utilisation enable_if), vous n'êtes plus dans un contexte déductible.

Mettre le enable_if dans le type de retour ou comme paramètre de modèle fonctionne en défaut à la place parce que les types de paramètres (en particulier les belles et simples std::vector<T>&) sont telles que T est déduisent.

+0

Merci, surtout pour l'explication quant à * pourquoi * ça ne marche pas – PhilLab