I took sample from http://www.cplusplus.com/reference/utility/forward:
// forward example
#include <utility> // std::forward
#include <iostream> // std::cout
// function with lvalue and rvalue reference overloads:
void overloaded (const int& x) {std::cout << "[lvalue]";}
void overloaded (int&& x) {std::cout << "[rvalue]";}
// function template taking rvalue reference to deduced type:
template <class T> void fn (T&& x) {
overloaded (x); // always an lvalue
overloaded (std::forward<T>(x)); // rvalue if argument is rvalue
}
int main () {
int a;
std::cout << "calling fn with lvalue: ";
fn (a);
std::cout << '\n';
std::cout << "calling fn with rvalue: ";
fn (0);
std::cout << '\n';
return 0;
}
which prints
calling fn with lvalue: [lvalue][lvalue]
calling fn with rvalue: [lvalue][rvalue]
But if I want to make overloaded template, like that:
template<typename T>
void overloaded (const T& x) {std::cout << "[lvalue]";}
template<typename T>
void overloaded (T&& x) {std::cout << "[rvalue]";}
it prints
calling fn with lvalue: [rvalue][rvalue]
calling fn with rvalue: [rvalue][rvalue]
Is there any way to use template function to deduct what an object I've transfered to function?
Yes it's possible, just add a const
to your second overload:
template<typename T>
void overloaded (const T& x);
template<typename T>
void overloaded (const T&& x);
// ^^^^^
The reason why you need to const
is to make x
not a forwarding reference. Forwarding references are very greedy, and if you don't pass in the exact same type (including any cv qualifiers) then the forwarding reference overload will get chosen.
In your case, because you do not pass a const
object to overload
, the second overload will always be a better match.
But if you add a const
there, then it's not a forwarding reference anymore and can only accept rvalues and no lvalues, and won't be a better match for an lvalue as a result but will be a better match for any rvalues than the const&
overload.
If you need to move from x
, then you will have to do something else. Remove the const&
overload and branch in the forwarding reference whether you have an rvalue or lvalue:
template <typename T> void overloaded(T &&x) {
if /*constexpr*/ (std::is_lvalue_reference_v<T>)
std::cout << "[lvalue]";
else
std::cout << "[rvalue]";
}
Note: You'll need to use if constexpr
if you do specific stuff that is not valid for a branch or the other.