Opérateur de surcharge <<: impossible de lvaluer sur std :: basic_ostream && ‘

J’ai une classe qui utilise une classe nestede et qui souhaite utiliser l’ operator<< classe nested operator<< pour définir un operator<< dans la classe supérieure. Voici à quoi ressemble mon code:

 #include  #include  template struct classA { struct classB { template friend inline std::ostream& operator<< (std::ostream &out, const typename classA::classB &b); }; classB root; template friend std::ostream& operator<< (std::ostream &out, const classA &tree); }; template inline std::ostream& operator<< (std::ostream &out, const classA &tree) { out << tree.root; return out; } template inline std::ostream& operator<< (std::ostream &out, const typename classA::classB &b) { return out; } int main() { classA a; std::cout << a; } 
  • Lors de la compilation sans support pour C ++ 11, la définition de operator << pour la classe interne ne semble pas être trouvée par le compilateur:

     so.hpp:24:7: error: no match for 'operator<<' in 'out << tree.classA::root' so.hpp:24:7: note: candidates are: ... 
  • Avec GCC 4.6 et 4.7 lors de la compilation avec std = c ++ 0x:

     so.hpp:21:3: error: cannot bind 'std::ostream {aka std::basic_ostream}' lvalue to 'std::basic_ostream&&' In file included from /usr/include/c++/4.7/iostream:40:0, from so.hpp:2: /usr/include/c++/4.7/ostream:600:5: error: initializing argument 1 of 'std::basic_ostream& std::operator<<(std::basic_ostream&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits; _Tp = classA::classB]' 

Quelqu’un peut-il me dire pourquoi ce code n’est pas légal et quelle est la meilleure façon de faire ce que je veux?

Bo a fourni la raison pour laquelle cela se produit (le type T n’est pas déductible dans l’appel à l’ operator<< nested operator<< . Une solution simple pour cela, et quelque chose que je recommande en général, pas seulement ici, n'est pas de se lier à un modèle, mais plutôt une seule fonction gratuite, pour cela vous devrez définir la fonction inline:

 template struct classA { struct classB { friend inline std::ostream& operator<< (std::ostream &out, const classB &b) { // definition goes here } }; classB root; friend std::ostream& operator<< (std::ostream &out, const classA &tree) { // definition goes here } }; 

Il existe quelques différences entre les deux approches. Le plus important est que cette approche obligera le compilateur à définir une surcharge pour l' operator<< non basée sur un modèle ”pour chaque instanciation du modèle, ce qui, comme il ne s'agit plus d'un modèle, ne dépend pas de la déduction des arguments. Un autre effet secondaire est que l’approche est un peu plus ssortingcte (vous êtes seulement ami avec une fonction, alors que dans votre approche initiale, vous vous êtes lié d'amitié avec le modèle et toutes les instanciations possibles (qui peuvent être utilisées pour accéder aux classes internes). les fonctions ainsi définies ne seront trouvées que via ADL, il y a donc moins de surcharges d' operator<< à prendre en compte lorsque l'argument n'est pas ClassA ou ClassA::ClassB .


Comment accéder à votre approche

 namespace { struct intruder { ClassA & ref; intruder( ClassA& r ) : ref(r) {} }; template <> std::ostream& operator<< ( std::ostream& _, ClassA const& i ) { std::cout << i.ref.private_member << std::endl; return _; } } 

Alternative

Sinon, vous pouvez devenir ami avec une spécialisation particulière d'un modèle. Cela résoudra le problème d' intruder , car il ne sera ouvert qu'à l' operator<< à ClassA , ce qui a un impact beaucoup moindre. Mais cela ne résoudra pas votre problème particulier, car le type ne serait toujours pas déductible.

Vous avez un problème avec un “contexte non déductible” dans cet opérateur

 template inline std::ostream& operator<< (std::ostream &out, const typename classA::classB &b) { return out; } 

Le compilateur ne peut pas déterminer quelles valeurs de T entraîneront un classB qui correspond au paramètre que vous souhaitez transmettre. Donc, ce modèle n’est pas pris en compte!

En mode C ++ 11, le compilateur recherche ensuite une correspondance étroite à partir de la bibliothèque standard

 operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) 

où il peut assortir _Tp à peu près n’importe quel type, y compris classA::classB , mais note que le premier paramètre ne correspond pas.

Essaye ça:

 template inline std::ostream& operator<< (std::ostream &out, const classA &tree) { //out << tree.root; ::operator<<( out, tree.root); return out; } 

et vous obtiendrez une confession d'ineptie directe:

 test.cpp:34:3: error: no matching function for call to 'operator<<(std::ostream&, const classA::classB&)' test.cpp:34:3: note: candidates are: test.cpp:23:22: note: template std::ostream& operator<<(std::ostream&, const typename classA::classB&) test.cpp:30:22: note: template std::ostream& operator<<(std::ostream&, const classA&) 

Solution de contournement: vous pouvez peut-être utiliser une fonction membre dans classB nested et l'utiliser à la place d'opérateur << ... Bien sûr, cette solution présente une multitude d'inconvénients, mais vous risquez de vous dépêcher.