为什么cout&lt;&lt; &#34;你好&#34;选择运算符的非成员版本&lt; <! - ?

时间:2016-09-05 19:42:30

标签: c++ operator-overloading iostream

今天我试图了解与运算符&lt;&lt; 及其重载相关的事情。

让我们来看看这段代码:

cout.operator<<("hello");   // +16 overloads -> implicit conversion to void*
cout.operator<<(123);       // +16 overloads

operator<<(cout,"hello");   // +13 overloads
operator<<(cout, 123);      // ERROR: no overload

cout << "hello";            // +13 overloads ---> non-member version!
cout << 123;                // +16 overloads ---> member version!

感谢Visual Studio的Intellisense,我可以检测到它可以提供多少重载。

我得出结论:

  • 非会员运营商&lt;&lt;重载 13
  • 会员运营商&lt;&lt;重载(对于cout) 16

我收到了以下问题:

从最后两行可以看出,当使用带有const char []的cout时,会选择非成员重载。

  • 为什么没有会员运营商&lt;&lt;对于采用const char []?
  • 的cout对象

另外,在第四行我们得到一个错误,因为没有非成员运营商&lt;&lt;带整数的重载。

  • 为什么没有非会员运营商&lt;&lt;需要一个整数?

最后但并非最不重要

  • 为什么cout << “hello”选择运营商的非会员版本&lt;&lt; ?

可能是因为有一个特定的非成员运营商&lt;&lt; overload 是const char [] 的一个很好的候选者,而不是成员运算符&lt;&lt;过载需要 void *

3 个答案:

答案 0 :(得分:3)

  

为什么operator<<对象的成员cout需要const char[]

字符串分开处理。为std::basic_string创建成员运算符将要求所有流都具有std::basic_string功能的依赖性,这并不总是可取的。所有成员运算符都是内置语言类型,但std::basic_string是一个类,因此它具有非成员运算符重载。有人可能会说const char[](衰变到const char*)是内置类型,但operator<<const char*std::basic_string类似,并且它没有&# 39;将这些实现拆分到库的两个不同区域是有意义的。处理字符串的功能(无论是使用数组还是类)通常组合在一起。

  

为什么没有非会员运营商&lt;&lt;需要一个整数?

因为不需要成为一个人。它已作为成员运营商存在。您不应该直接调用成员运算符(除非您需要,而您的示例不是)。您应该正常使用运算符(stream << ...)并让重载决策根据参数,作用域等选择正确的成员或非成员运算符。如果您考虑它,非成员重载将导致一个ambiquity错误。 stream << 123应该stream.operator<<(123)还是::operator<<(stream, 123)?只能有一个选择,否则编译失败。

  

为什么cout << “hello”选择operator<<的非会员版本?   也许是因为有一个特定的非成员operator<<重载,它是const char[] 的良好候选者,而不是成员 - operator<<重载,它需要{ {1}}?

这就是原因。类型化void*参数更适合窄字符串文字,然后是无类型const char[]指针。因此,如果两个运算符都在范围内(并且void*重载仅在范围内,如果使用const char[]),则编译器将选择更好的匹配重载。

答案 1 :(得分:2)

IIRC const char *超载的非成员性是由于技术原因:委员会希望宽字符流也提供const char*输出,但如果同时提供operator<<(const charT*)operator<<(const char *) bool TestCirclesCollision(double x1, double y1, double r1, double x2, double y2, double r2) { // Pythagorean theorem to compute distance between two points var dx = x2 - x1; var dy = y2 - y1; var distance = Math.Sqrt(dx * dx + dy * dy); // compare to combined radii of two circles // return true if collision, otherwise false return distance <= r1 + r2; } 作为成员,那么由于具有相同签名的两个成员函数,窄字符流将是格式错误的。将它们作为非成员解决了这个问题 - 他们现在只是在重载决策中解决它,如果存在歧义,你只需添加一个更专业的重载。

答案 2 :(得分:1)

  

为什么没有成员运营商&lt;&lt;对于带有const char []的cout对象?

没有必要,因为const char[]会衰减到const char*

  

为什么没有非会员运营商&lt;&lt;需要一个整数?

在ADL期间,这不会让我们产生歧义吗?

  

为什么cout&lt;&lt; “hello”选择运算符的非成员版本&lt;&lt; ?

因为它比转换为void const*更好,因此需要进行类型转换。