Actually, this is a very good question. So far I have also been using the universal reference trick plus the enable_if
hammer. Here I present a solution that doesn't make use of templates and uses lvalue cast as an alternative.
What follows is a real example where the situation arises using the known example of inplace ofstream
usage that is not possible (or very hard) in C++98 (I use ostringstream
in the example to make it more clear).
First you will see a function on lvalue reference as often seen in C++98.
#include<iostream>
#include<sstream>
struct A{int impl_;};
std::ostringstream& operator<<(std::ostringstream& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // possibly much longer code.
return oss;
}
// naive C++11 rvalue overload without using templates
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // ok, but there is code repetition.
return oss;
}
int main() {
A a{2};
{// C++98 way
std::ostringstream oss;
oss << a;
std::cout << oss.str() << std::endl; // prints "A(2)", ok"
}
{// possible with C++11, because of the rvalue overload
std::cout << (std::ostringstream() << a).str() << std::endl; //prints "A(2)", ok
}
}
As you can see in C++11 we can achieve what we can't in C++98. That is to make use of the ostringstream
(or ofstream
) inplace. Now comes the OP question, the two overloads look very similar, can both be joined in one?
One option is to use universal reference (Ostream&&
), and optionally with enable_if
to constrain the type. Not very elegant.
What I found by using this "real world" example is that if want to use the same code for lvalue ref and rvalue ref is because probably you can convert one to the other!
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
return operator<<(oss, a);
}
This looks like an infinitely recursive function, but it is not because oss
is an lvalue reference (yes, it is an lvalue reference because it has a name). So it will call the other overload.
You still have to write two functions but one has a code that you don't have to maintain.
In summary, if "it makes sense"© to apply a function both to a (non const) lvalue reference and rvalue that also means that you can convert the rvalue into an lvalue and therefore you forward to a single function. Note that the "it makes sense" depends in the context and the meaning of the intended code, and it is something that we have to "tell" the compiler by explictly calling the lvalue overload.
I am not saying that this is better than using universal reference, I say it is an alternative and arguably the intention is more clear.
Editable code here: http://ideone.com/XSxsvY. (Feedback is welcomed)