Operador de sobrecarga <<: no puede vincular lvalue a 'std :: basic_ostream &&’

Tengo una clase que usa una clase anidada, y quiero usar el operator<< clase nested operator<< para definir el operator<< en la clase superior. Así es como se ve mi código:

 #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; } 
  • Al comstackr sin soporte para C ++ 11, la definición de operador << para la clase interna parece no ser encontrada por el compilador:

     so.hpp:24:7: error: no match for 'operator<<' in 'out << tree.classA::root' so.hpp:24:7: note: candidates are: ... 
  • Con GCC 4.6 y 4.7 al comstackr con 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]' 

¿Puede alguien decirme por qué este código no es legal y cuál es la mejor manera de hacer lo que quiero?

Bo proporcionó la razón por la que esto está sucediendo (el tipo T no es deducible en la llamada al operator< < nested operator< < . Una solución simple para esto, y algo que recomiendo en general, no solo aquí, no es hacer amistad con una plantilla, sino más bien una única función libre. Para eso, necesitarás definir la función en línea:

 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 } }; 

Hay un par de diferencias entre los dos enfoques. El más importante es que este enfoque hará que el comstackdor defina una sobrecarga sin plantilla para el operator< < para cada instanciación de la plantilla, que como ya no es una plantilla, no depende de deducir los argumentos. Otros efectos secundarios son que el enfoque es un poco más estricto (solo se está haciendo amigo de una función, mientras que en su enfoque inicial se hizo amigo de la plantilla y todas las instancias posibles (que pueden usarse como una laguna para acceder a su clase interna). las funciones así definidas solo se encontrarán a través de ADL, por lo que hay menos sobrecargas del operator< < para que el comstackdor ClassA en cuenta cuando el argumento no es ClassA o ClassA::ClassB .


Cómo se puede obtener acceso con su enfoque

 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 _; } } 

Alternativa

Alternativamente, puede hacerse amigo de una especialización particular de una plantilla. Eso resolverá el problema del intruder , ya que solo estará abierto para el operator< < a ClassA , que tiene un impacto mucho menor. Pero esto no resolverá su problema en particular, ya que el tipo aún no sería deducible.

Tiene un problema con un “contexto no deducible” en este operador

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

El comstackdor no puede determinar qué valores de T resultarán en una classB que coincida con el parámetro que desea aprobar. ¡Entonces esta plantilla no es considerada!

En el modo C ++ 11, el comstackdor luego busca una coincidencia cercana de la biblioteca estándar

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

donde puede hacer coincidir _Tp con cualquier tipo, incluida classA::classB , pero observa que el primer parámetro no coincide.

Prueba esto:

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

y luego obtendrás una confesión directa de ineptitud:

 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&) 

Solución: quizás pueda usar una función de miembro en el classB nested, y usarla en lugar del operador < < ... Por supuesto, esa solución tiene una multitud de inconvenientes, pero puede sacarlo de esta prisa.