4

Typically, in C++, we used to define a custom ostream operator<< this way:

class A {
    int impl_;
    friend std::ostream& operator<<(std::ostream& os, A const& self){
       return os << "A:" << self.impl_;
    }
};

However now, post C++11, there are r-value references, and as a matter of fact, built-in types can be streamed to r-value std::ostream references. Now this is allowed:

int i = 5;
std::ofstream("file") << i;

(I don't know if this is the reason the special overloads were defined.)

Does it mean that for consistency one should define both operators for custom classes? Like this,

class A {
    int impl_;
    friend std::ostream& operator<<(std::ostream& os, A const& self) {
       return os << "A:" << self.impl_;
    }
    friend std::ostream&& operator<<(std::ostream&& os, A const& self) {
       os << "A:" << self.impl_; 
       return std::move(os);
    }
};

or more streamlined,

class A {
    int impl_;
    friend std::ostream& operator<<(std::ostream& os, A const& self) {
       return os << "A:" << self.impl_;
    }
    friend std::ostream&& operator<<(std::ostream&& os, A const& self) {
       return std::move(os << self); // calls the other overload
    }
};

What is the recommended way to overload operator<< nowadays in C++11?

alfC
  • 14,261
  • 4
  • 67
  • 118
  • I don't think the extra overload brings any benefits. – cpplearner Apr 16 '17 at 05:33
  • Off topic, but calling `std::move` from a return doesn't do anything at all, it just makes it difficult to do [unnamed] return value optimization and copy elision. Just return the value itself. – Mário Feroldi Apr 16 '17 at 05:34
  • @cpplearner that can be argued about. However that overload was defined for built-in types, there must be a logic for that. I don't think it is a bad idea to allow using the just created stream and be done with it. – alfC Apr 16 '17 at 09:24
  • 1
    Related: http://stackoverflow.com/questions/8828973/why-does-the-rvalue-overload-of-operator-for-basic-ostream-return-an-lvalu – chtz Apr 16 '17 at 10:15
  • 1
    @MárioFeroldi That may be good advice for a function that returns a class type by value, but the `std::move` is required for a function as here that returns an rvalue reference. – aschepler Feb 09 '18 at 06:01
  • @aschepler Functions should indeed not return rvalue references, just by value — C++ can avoid any copies in that case. – Blaisorblade Apr 21 '22 at 23:20
  • 2
    @Blaisorblade Usually, but there are some cases where returning rvalue via `std::move` is better. The template `std::operator` which takes any rvalue ostream (and makes the extra overload in this question unnecessary) returns by rvalue reference. If it returned by `std::ostream` value, the language would be forced to move-construct the return value, which does not take the `rdbuf()` with it, so a second insertion in a chain would fail. – aschepler Apr 23 '22 at 01:50
  • @aschepler yes, I agree but even before that, more fundamentally, when people say not to return by such and such in C++ they have to take into account the possibility of returning function arguments or objects that use the resources provided by function arguments. This is one of the cases. – alfC Apr 23 '22 at 05:48

1 Answers1

1

Besides the conceptual discussion, from a technical viewpoint:

After some experimentation I realized that I don't need to overload for the r-value ostream, the library does it for me already.

One only has to have the l-value version and the library has some r-value version that is forwarded to the implemented overload, which presumably works for any std namespace argument of operator << through ADL or something.

So, in C++11, this is allowed std::ofstream{"file"} << a, even if no r-value overload (for the stream) is custom defined.

Which seems to make operator<< quite special in STL.

Corrections are welcome.

alfC
  • 14,261
  • 4
  • 67
  • 118