为什么cout << "hello"选择operator<<的非成员版本?

Why does cout << "hello" choose the non-member version of the operator<<?

今天我试图了解与 operator<< 及其重载相关的事情。

让我们看一下这段代码:

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,我可以检测到每一个它可以提供多少重载。

我得出的结论是:

我有这些问题:

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

此外,在第四行我们得到一个错误,因为没有非成员运算符<< 重载需要一个整数。

最后但并非最不重要的一点

可能是因为有一个特定的非成员运算符<<重载 这是 const char[] 的一个很好的候选者,而不是成员运算符<<重载需要 void*?

Why isn't there a member operator<< for the cout object which takes a const char[]?

不需要,因为 const char[] 会衰减到 const char*

Why isn't there a non-member operator<< which takes an integer?

这不会让我们在日常生活中变得模棱两可吗?

Why does cout << “hello” choose the non-member version of the operator<< ?

因为它比转换为 void const* 更匹配,后者需要类型转换。

Why isn't there a member operator<< for the cout object which takes a const char[]?

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

Why isn't there a non-member operator<< which takes an integer?

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

Why does cout << “hello” choose the non-member version of the operator<< ? Maybe because there is a particular non-member operator<< overload which is a good candidate for a const char[], rather than the member-operator<< overload which takes a void*?

这正是原因所在。类型化的 const char[] 参数比非类型化的 void* 指针更适合窄字符串文字。因此,如果两个运算符都在范围内(并且 const char[] 重载仅在使用 #include <string> 时才在范围内),那么编译器将选择更匹配的重载。

IIRC const char * 重载的非成员性是由于技术原因:委员会希望宽字符流也提供 const char* 输出,但如果您同时提供 operator<<(const charT*)operator<<(const char *) 作为成员,那么窄字符流将是格式错误的,因为它具有两个具有相同签名的成员函数。让他们成为非成员解决了这个问题——他们现在只会在重载解决方案中解决这个问题,如果有歧义,你只需添加一个更专业的重载。