【问题标题】:Why does cout << "hello" choose the non-member version of the operator<<?cout << "hello" 为什么选择非会员版的operator<<?
【发布时间】:2017-01-13 04:32:10
【问题描述】:

今天我试图理解这个与 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,我可以检测每个它可以提供多少重载。

我得出结论:

  • 非成员运算符13
  • 成员运算符16

我得到了这些问题:

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

  • 为什么没有成员运算符

另外,在第四行我们得到一个错误,因为没有非成员运算符

  • 为什么没有接受整数的非成员运算符

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

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

可能是因为有一个特定的非成员运算符是 const char[] 的良好候选者,而不是成员运算符无效*?

【问题讨论】:

  • 为什么重要?
  • 因为我正在深入学习语言并想了解更多,也许吧?
  • 我真的很惊讶以前没有人问过这个问题。没有受骗的迹象。不过,对于工作组的邮件列表来说,这似乎是一个更好的问题;这里没有实际问题需要解决。您只是想知道为什么 IOStreams 的一些 operator&lt;&lt; 是成员,而一些是免费函数。
  • 这背后没有什么伟大的设计,只是一些运营商一直是会员 - 一直以来。添加新的非成员比添加成员要容易得多。无论如何,它们对cout &lt;&lt; x; 的工作方式相同。
  • 13 和 16 代表什么?

标签: c++ operator-overloading iostream


【解决方案1】:

为什么没有cout 对象的成员operator&lt;&lt; 接受const char[]

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

为什么没有非成员运算符

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

为什么cout &lt;&lt; “hello” 选择operator&lt;&lt; 的非会员版本? 可能是因为有一个特定的非成员 operator&lt;&lt; 重载它是 const char[] 的良好候选者,而不是采用 void* 的成员-operator&lt;&lt; 重载?

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

【讨论】:

  • 感谢详尽的回答。关于第一个问题,我仍然对原因有疑问。您的意思是说不需要在 ostream::operator
  • 我是说它会强制对所有流类型的字符串类进行不必要的链接。甚至那些不需要字符串处理的。字符串处理是使用非成员运算符作为插件实现的,就像任何其他用户定义的类一样。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    为什么 cout 对象没有一个成员运算符

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

    为什么没有非成员运算符

    这不会让我们在 ADL 期间变得模棱两可吗?

    为什么cout

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

    【讨论】:

    • 仍然不相信第一个问答。您说 const char[] 衰减为 const char*。是的,这是众所周知的数组衰减为指针的行为。但问题仍然是一样的为什么没有成员运算符
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-07
    • 2018-01-04
    • 2013-02-18
    • 1970-01-01
    • 2011-06-08
    相关资源
    最近更新 更多