template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}
int main()
{
int x;
f(x); //ambiguous
}
Why is this call ambiguous? The first template specialization is f<int>
(int&), and the second is f<int&>
(int&). As the parameters are the same, the function template, which is more specialzed according to the partial ordering rules, is better. Then according to Standard 14.8.2.4/9
If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):
— if the type from the argument template was an lvalue reference and the type from the parameter template was not, the argument type is considered to be more specialized than the other; ...
The first template has T& and the second has T&&, so the first should be more specialized. What is wrong here?
Edit: This code is tested in g++ 4.6.1 and VC++ 2010 Express, both give the ambiguous error.
Guideline:
Do not overload:
template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}
Reason:
There is a special template deduction rule for the pattern:
template <typename T> void f(T&&) {}
This rule exists in order to enable so called "perfect forwarding". It helps things like bind
and make_shared
forward their arguments perfectly, preserving both cv-qualifiers and "value category" (lvalue/rvalue-ness).
This special rule says that when f(T&&)
is called with an lvalue parameter (e.g. int
), that T gets deduced as an lvalue reference (e.g. int&
) instead of int
. And an rvalue reference to lvalue reference to int collapses down to just lvalue reference to int. I.e.
f(x)
calls
f<int&>(int& && x);
which simplifies to:
f<int&>(int& x);
Edit
This is not more or less specialized than f<int>(int&)
.
Thanks to Johannes Schaub for the correction (see comments).
Solution:
You can do whatever you want with the single function:
template <typename T> void f(T&&) {}
If T
deduces as an lvalue reference, do whatever you wanted to do in your first overload, otherwise do whatever you wanted to do in your second overload:
template <class T> void f_imp(T&, std::true_type) {std::cout << "lvalue\n";}
template <class T> void f_imp(T&&, std::false_type) {std::cout << "rvalue\n";}
template <typename T> void f(T&& x)
{
f_imp(std::forward<T>(x), std::is_lvalue_reference<T>());
}
And use std::forward
to perfectly forward x
to the implementation-detail function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With