gcc 8.0.0 and clang 5.0.0 disagree on the behavior of this program:
#include <iostream>
template <typename T>
struct A {
A(const T&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
A(T&&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
};
template <typename U> A(U&&) -> A<double>;
int main() {
int i = 0;
const int ci = 0;
A a1(0); // both say A<double>
A a2(i); // both say A<double>
A a3(ci); // gcc says A<int>, clang says A<double>
}
gcc's behavior doesn't make sense to me - if the const T&
overload is preferred to the U&&
overload for lvalue const int
, why isn't the T&&
overload preferred to the U&&
overload for rvalue int
? clang's makes more sense to me (none of the functions is more specialized than the other, so deduction guide wins).
Who's right?
We are in partial-ordering land again. The type of the synthesized function template parameters are
T&& // #1: not a forwarding reference
const T& // #2
U&& // #3: a forwarding reference
The pre-partial ordering transformation strips away referenceness and after that the top-level cv-qualification, leaving us with a bare type in all three cases. It follows that in all three cases deduction succeeds in both directions. We are now left with [temp.deduct.partial]/9's tiebreaker:
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 parameter type is not considered to be at least as specialized as the argument type; otherwise,
- if the type from the argument template is more cv-qualified than the type from the parameter template (as described above), the parameter type is not considered to be at least as specialized as the argument type.
For U&&
vs T&&
, neither rule applies and there's no ordering. For U&&
vs const T&
, however, the parameter type U&&
is not considered to be at least as specialized as the argument type const T&
, per the first bullet.
Partial ordering therefore finds #2 to be more specialized than #3, but finds #1 and #3 to be indistinguishable. GCC is correct.
That said, this may well be an oversight in the partial ordering rules. Class template deduction is the first time we have a "rvalue reference to cv-unqualified template parameter that isn't a forwarding reference" thing. Previously, in double-reference cases, forwarding references will always lose to non-forwarding rvalue references at the second bullet (because the only way you get non-forwarding rvalue references is if you have cv T&&
for some non-empty cv
).
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