Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload resolution with universal reference involved

For the following code:

class A {};

template <typename T> void f(T& a) {}
template <typename T> void f(T&& a) {}

int main() {
    A a;
    f(a);
}

clang++ binds the call to the first overload, while g++ reports ambiguous call. Which one is taking the correct action?

like image 244
goodbyeera Avatar asked Mar 07 '14 10:03

goodbyeera


1 Answers

Both gcc 4.9.0 20140302 and clang 3.5 (trunk 202594) correctly pick the first version. As hvd kindly gave me the references in comments:

  • It was a subtle issue, there was a defect report,

  • The gcc implementation prior 4.9 implemented the earlier defect, see Template overload resolution ambiguous for T&& versus T&

  • The corresponding part in the standard is 14.8.2.4 [temp.deduct.partial], paragraph 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; otherwise,
— if the type from the argument template is more cv-qualified than the type from the parameter template (as described above), the argument type is considered to be more specialized than the other; otherwise,
— neither type is more specialized than the other.


By the way, watch The Universal Reference/Overloading Collision Conundrum video why overloading on universal references is a bad idea. In short, consider the following example:

#include <iostream>
#include <utility>

struct A { bool guts_stolen; };

void steal_guts(A&& a) {
  a.guts_stolen = true; 
}

template <typename T> void f(const T& a) { 
                          // ^^^^^ note the const!
  std::cout << "T&\n"; 
}

template <typename T> void f(T&& a) { 
  std::cout << "T&&\n";
  steal_guts(std::move(a));
}

int main() {
    A a{ false };
    f(a);
    std::cout << "Guts stolen? " << std::boolalpha << a.guts_stolen << std::endl;
}

If you run the program, it will print

T&&
Guts stolen? true

which is not at all what you would expect just by looking at A a{ false }; f(a);.

like image 151
Ali Avatar answered Nov 26 '22 13:11

Ali