简体   繁体   English

静态与成员运算符重载:std :: operator <<和std :: ostream :: operator <

[英]Static vs. Member Operator Overloads: std::operator<< and std::ostream::operator<<

The ostream class of C++ provides many default overloads for operator<< , however they are not all defined in the same way. C ++的ostream类为operator<<提供了许多默认重载,但它们并非都以相同的方式定义。

The overloads for char types, string types, and rvalue streams are defined as free namespace -scope functions such as: char类型, string类型和rvalue流的重载被定义为free namespace -scope函数,例如:

namespace std {
ostream &operator<<(ostream &os, char c);
}

While the overloads for arithmetic types, streambuf , and stream manipulators are defined as member functions of std::ostream such as: 虽然算术类型, streambuf和流操作streambuf重载被定义为std::ostream成员函数,例如:

namespace std {
ostream &ostream::operator<<(int val);
}

My Question 我的问题

Is there an reason for this distinction? 有这种区别的原因吗? I understand that calls to these operator overloads operate slightly differently (ie ADL for the free namespace -scope definitions), and so I'd imagine there might be a preference to a particular type of operator overload for optimization purposes. 我理解对这些运算符重载的调用操作稍有不同(即空闲namespace -scope定义的ADL),因此我认为出于优化目的,可能存在对特定类型的运算符重载的偏好。 But here std::ostream uses both types of definitions for different types. 但是这里std::ostream对不同类型使用两种类型的定义。 Are there any advantages to this semantically or implementation optimizations that this allows for? 这允许的语义或实现优化是否有任何优势?

I'd imagine there might be a preference to a particular type of operator overload for optimization purposes 我想,出于优化目的,可能会偏好特定类型的运算符重载

Well, no. 好吧,不。 At the end of the day both are preformed as function calls. 在一天结束时,两者都作为函数调用执行。 There is not even an apparent implication to overload resolution itself. 重载决策本身甚至没有明显的含义。 Since the standard dictates at [over.match], paragraphs 2 and 6 : 由于标准规定在[over.match],第2和第6段:

If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator. 如果任一操作数具有类或枚举类型,则可以声明用户定义的操作符函数来实现此运算符,或者可能需要用户定义的转换将操作数转换为适合于构造函数的类型。在运营商。 In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator. 在这种情况下,重载决策用于确定调用哪个运算符函数或内置运算符来实现运算符。

The set of candidate functions for overload resolution is the union of the member candidates, the non-member candidates, and the built-in candidates. 重载决策的候选函数集是成员候选者,非成员候选者和内置候选者的联合。 The argument list contains all of the operands of the operator. 参数列表包含运算符的所有操作数。 The best function from the set of candidate functions is selected according to [over.match.viable] and [over.match.best]. 根据[over.match.viable]和[over.match.best]选择候选函数集中的最佳函数。

All of those operator overloads are resolved together. 所有这些操作员重载都一起解决。 The only semantic difference is that a class deriving from ostream may choose to hide certain member overloads. 唯一的语义差异是从ostream派生的类可能会选择隐藏某些成员重载。 This is done with accordance to how overloading works in a derived class. 这是根据派生类中的重载方式来完成的。 Only the overloads explicitly declared will be applicable. 只有明确声明的重载才适用。 Unlike those members, the free function overloads will always participate in overload resolution, even for classes that derive from ostream . 与那些成员不同,自由函数重载将始终参与重载解析,即使对于从ostream派生的类也是如此。

Since a derived class needs to be converted to an ostream& in order for a free-function overload to be chosen, its own implicit conversion sequence needs to be ranked. 由于派生类需要转换为ostream&为了选择自由函数重载,因此需要对其自己的隐式转换序列进行排序。 And it may cause ambiguities if all the overloads are free functions. 如果所有重载都是自由函数,则可能会导致歧义。

So the consideration may very well be to separate the types which may cause an ambiguity (pointers and arithmetic types) from the useful types which we may always want to have available (pointers to C-strings and individual characters). 因此,考虑很可能是将可能导致歧义(指针和算术类型)的类型与我们可能总是希望可用的有用类型(指向C字符串和单个字符的指针)分开。 And allow hiding the "less useful" ones so as to avoid those ambiguities. 并允许隐藏“不太有用”的那些,以避免这些含糊不清。


As pointed out by WF ostream is in fact basic_ostream<char> . 正如WF指出的那样, ostream实际上是basic_ostream<char> The free functions just so happen to be for data that requires streaming only. 自由函数恰好适用于仅需要流式传输的数据。 Characters or strings in the streams native "alphabet". 流本地“字母”中的字符或字符串。 So for basic_ostream<wchar_t> those free functions will accept wchar_t and wchar_t* . 因此对于basic_ostream<wchar_t>这些自由函数将接受wchar_twchar_t* It's quite possible that simple streaming doesn't require any access to the streams private section. 简单的流媒体很可能不需要访问流私有部分。

The other overloads are for data that requires serialization before streaming. 其他重载适用于在流式传输之前需要序列化的数据。 Since said serialization is tightly coupled with the streams internal state, it makes far more sense to make those overloads be members. 由于所述序列化与流内部状态紧密耦合,因此使这些重载成为成员更有意义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM