Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the rvalue overload of `operator<<` for `basic_ostream` return an lvalue reference?

Tags:

§27.7.3.9 defines the following overload for operator<<:

template <class charT, class traits, class T>   basic_ostream<charT, traits>&   operator<<(basic_ostream<charT, traits>&& os, const T& x); 

Effects: os << x
Returns: os

(§27.7.2.6 defines the rvalue overload for operator>>.)
Basically, it just forwards to an lvalue overload. I consider this overload to be pretty dangerous (the istream one even more so than the ostream one, actually), consider the following:

#include <sstream> #include <iostream>  int main(){   auto& s = (std::stringstream() << "hi there!\n");   std::cout << s.rdbuf(); // oops } 

Live example on Ideone (perfect example of undefined behaviour. Prints nothing for me on MSVC10).

The above example might look contrived, but it shouldn't be too hard to get into this situation in generic code or when passing the (std::stringstream() << "text") to a function that provides an lvalue and an rvalue overload and stores the std::ostream or std::istream in different ways according to the overload.

Now, what would be an argument agains returning a basic_ostream<charT, traits>&& and specifying the following?

Returns: move(os)

(And the same for basic_istream.)

Is there anything I'm overlooking? At the current state, in my eyes, it just looks dangerous and like a defect. I skimmed through the LWG issue list and found this proposal (hi @HowardHinnant!). It indeed returns an rvalue, however only for the added benefit of being able to chain this special operator, not specifically addressing the safety issue I described above (though it certainly does solve it). Additionally, it's marked as closed and for reconsideration for the next standard. As such, I thought I'd ask here:

Is there any good reason why the above mentioned overload returns an lvalue reference?

like image 301
Xeo Avatar asked Jan 12 '12 01:01

Xeo


People also ask

Is an rvalue reference an lvalue?

An lvalue is an expression that yields an object reference, such as a variable name, an array subscript reference, a dereferenced pointer, or a function call that returns a reference. An lvalue always has a defined region of storage, so you can take its address. An rvalue is an expression that is not an lvalue.

Can you pass an lvalue to an rvalue reference?

By overloading a function to take a const lvalue reference or an rvalue reference, you can write code that distinguishes between non-modifiable objects (lvalues) and modifiable temporary values (rvalues). You can pass an object to a function that takes an rvalue reference unless the object is marked as const .

Can rvalue bind to lvalue?

By default, the compiler cannot bind a non-const or volatile lvalue reference to an rvalue.

Why do we need rvalue reference?

Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.


1 Answers

It's a defect and it is my fault, sorry. LWG 1203 (thanks for finding that for me! :-)) is my current opinion of the correct fix for the "rvalue-stream-inserter". Note though that you could still catch it and be in trouble:

auto&& s = (std::stringstream() << "hi there!\n"); std::cout << s.rdbuf(); // oops 

Though at least in the above code it is a little more obvious (because of the &&) that you're doing something you shouldn't.

like image 153
Howard Hinnant Avatar answered Dec 07 '22 17:12

Howard Hinnant