重载运算符<<:不能将左值绑定到'std :: basic_ostream <char> &&'

我有一个使用嵌套类的类,并且想要使用嵌套类operator<<来定义上层类中的operator<< 。 以下是我的代码的样子:

 #include <memory> #include <iostream> template<typename T> struct classA { struct classB { template<typename U> friend inline std::ostream& operator<< (std::ostream &out, const typename classA<U>::classB &b); }; classB root; template<typename U> friend std::ostream& operator<< (std::ostream &out, const classA<U> &tree); }; template<typename T> inline std::ostream& operator<< (std::ostream &out, const classA<T> &tree) { out << tree.root; return out; } template<typename T> inline std::ostream& operator<< (std::ostream &out, const typename classA<T>::classB &b) { return out; } int main() { classA<int> a; std::cout << a; } 
  • 当编译不支持C ++ 11时,编译器似乎找不到内部类的operator <<的定义:

     so.hpp:24:7: error: no match for 'operator<<' in 'out << tree.classA<int>::root' so.hpp:24:7: note: candidates are: ... 
  • 使用GCC 4.6和4.7编译std = c ++时0x:

     so.hpp:21:3: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&' 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<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = classA<int>::classB]' 

有人可以告诉我为什么这个代码是不合法的,什么是做我想要的最好的方式?

Bo提供了这种情况发生的原因 (typesT在调用嵌套operator<<不能被推导出来)。对于这个简单的解决方法,我推荐的一些东西,不仅在这里,而且不是模板的结合,而不是一个单一的自由函数,为此你需要定义函数内联:

 template<typename T> 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<U> &tree) { // definition goes here } }; 

这两种方法有一些差异。 最重要的是,这种方法将使编译器为operator<<为模板的每个实例化定义一个非模板化的重载,因为它不再是模板,不依赖于推导参数。 另一个副作用是方法稍微紧凑一点(你只是交朋友,而在你最初的方法中,你和模板以及所有可能的实例(可以被用来作为漏洞进入你的类内部)结为好友。这样定义的函数只能通过ADLfind,所以当参数不是ClassA<T>ClassA<T>::ClassB时,编译器要考虑的operator<<重载less。


你的方法如何获得

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

替代

另外,你也可以和一个特定的模板专门化。 这将解决intruder问题,因为它只会对operator<< to ClassA<intruder> ,这个影响会小得多。 但这并不能解决你的问题,因为这个types还不能被推断。

你在这个操作符中有一个“不可推论的上下文”的问题

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

编译器无法确定T值是否会导致与要传递的参数匹配的classB 。 所以这个模板不被考虑!

在C ++ 11模式下,编译器继续从标准库中find一个紧密的匹配

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

可以匹配_Tp到几乎任何types,包括classA<T>::classB ,但注意到第一个参数不匹配。

尝试这个:

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

然后你会得到一个坦白的坦白的坦白:

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

解决方法:也许你可以在嵌套的classB中使用成员函数,并使用它来代替运算符<< …当然,这个解决scheme有很多缺点,但是它可能会让你匆匆离开。