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?
const
(istream
is modified while read and the handler changes its state during callbacks).&&
) 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.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.
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.