-->

invalid initialization of non-const reference from

2020-07-23 04:35发布

问题:

So I have the following function:

void scan(std::istream& is, Handler& h);

I want to call it in different ways, like:

scan(std::cin, Handler());
scan(std::ifstream("myfile"), myhandler);

The compiler complains about std::ifstream("myfile") and Handler() of being rvalues being passed as non-const references, so the complaint is legitimate, but what can I do?

  1. Neither function parameters cannot be const (istream is modified while read and the handler changes its state during callbacks).
  2. If I change the parameter types to rvalue references (&&) then I will not be able to pass std::cin and sometimes I really care about the final state of myhandler thus I cannot apply std::move on them neither.
  3. In principle I could make the parameters as universal references via template or auto&& type deduction and thus overload this function for all possible combinations of lvalue and rvalue references, but I have no intention of overloading this function for other types than I have already specified.

Are there any other options?

Somehow this whole move semantics got in the way in such a trivial example.

回答1:

To convert an rvalue to an lvalue, you can use this lvalue helper function:

template<class T>
T& lvalue_ref(T&& x) { return x; }

And then the call becomes:

scan(lvalue_ref(std::ifstream("myfile")), lvalue_ref(Handler()));

This is safe as the temporaries (the ifstream and Handler) aren't destructed until the end of the full expression. However, note that these are lvalue references to temporaries and as such you must use caution when deciding to use this method. I'm assuming the scan() doesn't hold references/pointers to the arguments after it returns.

For example, do not use it like this:

int& x = lvalue_ref(5);
std::cout << x; // temporary is destructed, therefore Undefined Behavior

Just make sure the lifetime of the returned reference corresponds with the lifetime of the temporary, and you'll be fine.