samedi 19 août 2017

Partial ordering of function templates - ambiguous call to overloaded function

Consider the following example:

#include <iostream>

template<class T>
struct A {};
struct B : public A<int> {};
struct C {
  operator B() { return {}; }
};

template<typename U> struct identity { typedef U type; };

template<class T> using identity_t = typename identity<T>::type;

template<class X>
void test(A<X> arg1, A<X> arg2) {              // #1
  std::cout << "ok1";
}

template<class X>
void test(A<X> arg3, identity_t<A<X>> arg4) {  // #2
  std::cout << "ok2";
}

int main() {
  B a, b;
  C c;
  test(a, b);
  test(b, c);
}

It works fine in GCC and clang (output ok1ok2) but fails in MSVC 2017: Live demo on Godbolt

27 : <source>(27): error C2668: 'test': ambiguous call to overloaded function
20 : <source>(20): note: could be 'void test<int>(A<int>,A<int>)'
15 : <source>(15): note: or       'void test<int>(A<int>,A<int>)'
27 : <source>(27): note: while trying to match the argument list '(B, B)'

Which kind of makes sense (although is surely a bug in MSVC;) and thus brings us to my questions about why and how it works in GCC and clang:

  1. How does test(a, b) select #1 while test(b, c) - #2? They look like equally good candidates at least in case test(a, b).

  2. Why isn't the compiler complaining about two test instantiations with an exact same signature?

Aucun commentaire:

Enregistrer un commentaire