Question

I understand the forwarding method in C++11.

template <class T> void foo(T &&)

foo will now accept both lvalues and rvalues.

My problem is when I overload foo furthermore. Consider this simple code:

template <class T> class A {};

template <class T> void foo(T &&obj) {}
template <class T> void foo(A<T> &&obj) {}

int main() {
  int i;
  A<int> a;
  foo(i); // calls foo(T &&) as I want
  foo(a); // calls foo(T &&), but I want it to call foo(A<T> &&)
  return 0;
}

If I call foo with an A < int > & (lvalue) object, it will call foo(T &&), and not foo(A < T > &&). In the definition of foo(T &&obj), I managed to differentiate with std and custom traits if obj is A<T> or not, but this produces very very messy code, as I must call the A method from obj, and obj is declared as T, and not as A < T >. Reordering declarations or adding lvalue overloads don't solve the problem.

Hope I made myself understood. I provided a simplified code in order to zone in the problem. I am implementing a custom Optional < T > class (similar to boost::optional) and I have this problem at constructors as I need to be able to create (and assign) an Optional < T > object from another Optional < T >, Optional < U >, T or U object. (where T is the type held by the optional object to be created, U is a different type, convertible to T).

Thank you for your time.

Was it helpful?

Solution

During template argument deduction for a function call, the special rules for "universal references" only apply if the parameter type is a cv-unqualified template parameter [temp.deduct.call]/3

[P is the parameter type of the function template, and A is the type of the argument]

If P is an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction. [Example:

template <class T> int f(T&&);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue

end example ]

Similarly, these rules do not apply to the parameter type A<T>&&. It is not a "universal reference" but purely an rvalue-reference type.


If you want to have a ranking between two constructors, where the A<T>-version shall be more specialized (a better match), you can:

  • provide twothree overloads A<T> const&, A<T>& (thanks, Eric Niebler) and A<T>&& in addition to the T&& generic version
  • use SFINAE; provide two overloads with a parameter T&& and check if T is a specialization of A<T>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top