ostream
C ++类为operator<<
提供了许多默认重载,但它们并非都以相同的方式定义。
char
类型的overloads,string
类型和rvalue流定义为免费的namespace
- 范围函数,例如:
namespace std {
ostream &operator<<(ostream &os, char c);
}
虽然算术类型overloads,streambuf
和流操作符被定义为std::ostream
的成员函数,例如:
namespace std {
ostream &ostream::operator<<(int val);
}
这种区别是否有原因?我理解对这些运算符重载的调用操作稍有不同(即空闲namespace
- 范围定义的ADL),因此我认为出于优化目的,可能会偏好特定类型的运算符重载。但是这里std::ostream
对不同类型使用两种类型的定义。这种语义或实现优化是否有任何优势?
答案 0 :(得分:5)
我认为出于优化目的,可能会偏好特定类型的运算符重载
嗯,不。在一天结束时,两者都作为函数调用执行。重载决策本身甚至没有明显的含义。由于标准规定在[over.match],段落2和6:
如果任一操作数的类型是类或枚举,则a 可以声明用户定义的操作符函数来实现它 可能需要运算符或用户定义的转换来转换 操作数适用于内置运算符的类型。在这 case,重载决策用于确定哪个运算符函数 或调用内置运算符来实现运算符。
重载解析的候选函数集是。的并集 会员候选人,非会员候选人和内置人员 候选人。参数列表包含的所有操作数 运营商。候选函数集中的最佳函数是 根据[over.match.viable]和[over.match.best]选择。
所有这些操作员重载都是一起解决的。唯一的语义差异是从ostream
派生的类可能会选择隐藏某些成员重载。这是根据派生类中的重载方式来完成的。只有明确声明的重载才适用。与那些成员不同,自由函数重载将始终参与重载解析,即使对于派生自ostream
的类也是如此。
由于需要将派生类转换为ostream&
以便选择自由函数重载,因此需要对其自己的隐式转换序列进行排序。如果所有重载都是自由函数,则可能会导致歧义。
因此,考虑很可能是将可能导致歧义(指针和算术类型)的类型与我们可能总是希望可用的有用类型(指向C字符串和单个字符的指针)分开。并允许隐藏“不太有用”的那些,以避免这些含糊不清。
正如W.F.所指出的那样。 ostream
实际上是basic_ostream<char>
。自由函数恰好适用于仅需要流式传输的数据。流本地“字母”中的字符或字符串。因此,对于basic_ostream<wchar_t>
,这些免费功能会接受wchar_t
和wchar_t*
。简单的流媒体很可能不需要访问流私有部分。
其他重载适用于在流式传输之前需要序列化的数据。由于所述序列化与流内部状态紧密耦合,因此使这些重载成为成员更有意义。