Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it reasonable to take std::istream&& as a argument?

I have encountered code which does this:

SomeObject parse (std::istream && input) {....

The input argument is an rvalue reference, which normally means the function is intended to take ownership of the argument. That's not quite what is happening here.

The parse function will completely consume the input stream, and it demands an rvalue reference because then calling code will give away ownership of the istream and hence this is a signal that the input stream will be unusable.

I think this is okay because, since the parse function doesn't actually move the object around, there is no danger of slicing out the subtype. This is basically behaving as a normal reference from parse's point of view, only there is a kind of compilable comment to the calling function that you have to give up ownership of the stream.

Is this code actually safe? Or is there some overlooked subtlety which makes this dangerous?

like image 538
spraff Avatar asked Jan 16 '19 15:01

spraff


2 Answers

std::move just produces an rvalue reference from an object and nothing more. The nature of an rvalue is such that you can assume nobody else will care about it's state after you're done with it. std::move then is used to allow developpers to make that promise about objects with other value categories. In other words calling std::move in a meaningful context is equivalent to saying "I promise I don't care about this object's state anymore".

Since you will be making the object essentially unusable and you want to make sure the caller won't use the object anymore using an rvalue reference enforces this expectation to some extent. It forces the caller to make that promise to your function. Failure to make the promise will result in a compiler error (assuming there isn't another valid overload). It does not matter if you actually move from the object or not, only that the original owner has agreed to forfeit it's ownership.

like image 93
François Andrieux Avatar answered Oct 19 '22 08:10

François Andrieux


An std::istream isn't moveable, so there's no practical benefit to this.

This already signals that the thing may be "modified", without the confusion of suggesting you're transferring ownership of the std::istream object (which you're not doing, and can't do).

I can kind of see the rationale behind using this to say that the stream is being logically moved, but I think you have to make a distinction between "ownership of this thing is being transferred", and "I keep ownership of this thing but I will let you consume all of its services". Ownership transfer is quite well understood as a convention in C++, and this is not really it. What will a user of your code think when they have to write parse(std::move(std::cin))?

Your way isn't "dangerous" though; you won't be able to do anything with that rvalue reference that you can't with an lvalue reference.

It would be far more self-documenting and conventional to just take an lvalue reference.

like image 24
Lightness Races in Orbit Avatar answered Oct 19 '22 08:10

Lightness Races in Orbit