9

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?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Barry
  • 286,269
  • 29
  • 621
  • 977
  • I'll take a stab at this. The deduction is for an rvalue (reference). `ci` is an lvalue of a `const`, and there happens to be a constructor that takes a reference to a `const` as a parameter. – Sam Varshavchik Jun 03 '17 at 17:28
  • Pardon my lack of knowledge, but how is this `->` syntax called? – HolyBlackCat Jun 03 '17 at 19:57

1 Answers1

7

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).

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • That all makes sense. Per the last paragraph, do you think the first bullet should cover non-forwarding rvalue reference too (i.e. preferring `T&&` to `U&&`)? – Barry Jun 03 '17 at 17:40
  • Iirc, overload resolution has tie breaker bullets that involves both partial ordering and checking whether one of two candidates was a deduction guide. It seems GCC and clang apply these tie breakers in different orders. I don't think it is about disagreement on partial ordering, but I am on android currently so I can't research. – Johannes Schaub - litb Jun 03 '17 at 20:11
  • @JohannesSchaub-litb You mean Clang doesn't implement [P0620](https://wg21.link/P0620)? Maybe. But the "why doesn't #1 beat #3 when #2 does" question is partial ordering. – T.C. Jun 03 '17 at 20:17
  • @JohannesSchaub-litb That actually seems plausible. In [this example](https://wandbox.org/permlink/GXzAHJA5kRz7v2v9), clang picks `A` (less specialized, user-declared deduction guide) and gcc picks `A` (more specialized, constructor) – Barry Jun 03 '17 at 20:40
  • @JohannesSchaub-litb Filed [llvm bug 33295](https://bugs.llvm.org/show_bug.cgi?id=33295). – Barry Jun 03 '17 at 20:55