Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

The overloads for char types, string types, and rvalue streams are defined as free namespace-scope functions such as:

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:

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 (i.e. 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. But here std::ostream uses both types of definitions for different types. Are there any advantages to this semantically or implementation optimizations that this allows for?

like image 294
define cindy const Avatar asked Sep 30 '17 18:09

define cindy const


People also ask

Can we overload << operator using the member function?

When overloading an operator using a member function: The overloaded operator must be added as a member function of the left operand. The left operand becomes the implicit *this object. All other operands become function parameters.

Which operator is overloaded in the ostream class?

To get cout to accept a Date object after the insertion operator, overload the insertion operator to recognize an ostream object on the left and a Date on the right. The overloaded << operator function must then be declared as a friend of class Date so it can access the private data within a Date object.

Can we overload << operator in C++?

Can we overload all operators? Almost all operators can be overloaded except a few. Following is the list of operators that cannot be overloaded. sizeof typeid Scope resolution (::) Class member access operators (.

What is Ostream operator in C++?

A function that takes and returns a stream object. It generally is a manipulator function.


1 Answers

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:

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].

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. 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.

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. 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). And allow hiding the "less useful" ones so as to avoid those ambiguities.


As pointed out by W.F. ostream is in fact 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*. 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.

like image 148
StoryTeller - Unslander Monica Avatar answered Nov 23 '22 07:11

StoryTeller - Unslander Monica