Question

Some operators cannot be overloaded as class members. One such example are the bitwise shift operators used for streams see here for example. The reason for that (as far as I understand it) seems to be that the left hand site operand is the one you define the behaviour for and you cannot define behaviour for the built-in streams. Please correct me if I'm wrong.

I think it would be useful to define those overloads as class members, because they likely need to access private data. Sure, there's friend, but that seems to be more of a workaround.

Now this is more of a hypothetical question, but why not swap the meaning of >> and << then?

obj >> std::cout;
obj << std::cin;

This way, the whole operation happens from the point of view of the object, not the stream. The overload can be made as a member function, because the object is the left hand operand. Would that be possible?

I guess there are a lot of things this would break, like readability for starters as everybody is used to the other way. What other things would this break?

Was it helpful?

Solution

Operator overloads in C++ are syntactic sugar, nothing more. Your example will work fine: however, consider two things. First, how do people expect stream operators (technically bitshift operators) to function? They expect them to be able to be chained: the second idea to consider is how does your example work with chaining?

Given this code:

std::cout << obj1 << obj2 << std::endl;

What is really going on is this, once you remove the syntactic sugar:

std::cout.operator<<(obj1).operator<<(obj2).operator<<(std::endl);

Remember, operator<<() returns a reference to *this by convention (true in the standard library, and a good idea in general to allow chaining operators this way). That means that all of those calls are invoked on std::cout, the first object referenced in that statement.

Now consider your example:

obj >> std::cout;

If you extend this to chain multiple objects, you might get this:

obj1 >> obj2 >> std::cout;

Which would look like this if converted to proper function calls:

obj1.operator>>(obj2).operator>>(std::cout);

What this means is yes, you can reverse the order, but it will break down and not behave the way C++ programmers expect once you chain multiple functions together.

OTHER TIPS

Swapping the direction of the I/O operators to move the operands to the left-hand side would be technically possible, but the "chaining" which is often used for I/O streaming would be at least cumbersome. Instead of

std::cout << "The result is " << result << endl;

you would have to write either

"The result is " >> std::cout;
result >> std::cout;
std::endl >> std::cout;

or

std::endl >> (result >> ("The result is " >> std::count));

which feels unnatural.

Along with the problems already cited (which mostly boil down to lack of chaining), you have another fundamental problem here. Consider what happens if you try to work with a type that's built into the compiler:

int x;

x << std::cin;

Lacking a free function overloaded to do the job, this would be trying to do x.operator<<(std::cin);. Since x is of type int, it doesn't and can't contain any overloaded operators.

So, for all the built-in types, you'd still have to implement the overload as a free function like you do now, or else build wrappers like Java's Integer just to handle these overloads. Neither seems (at least to me) to provide any meaningful improvement over the current situation.

Licensed under: CC-BY-SA with attribution
scroll top