linuxfever linuxfever - 3 months ago 18
C++ Question

Overload resolution with template parameters

I am having trouble understanding why the following leads to an ambiguous call:

#include <iostream>

// generic version f(X, Y)
template <class X, class Y>
void f(X x, Y y) {
std::cout << "generic" << std::endl;
}

// overload version
template <class X>
void f(X x, typename X::type y) {
std::cout << "overload" << std::endl;
}

struct MyClass {
using type = int;
};

int main() {
f(MyClass(), int()); // Call to f is ambiguous
}


I would expect the overload version, which is more specialised in the second argument than the generic version, to be selected as the best candidate. I know that if I change the overload version to

template <class X>
void f(X x, int y) {
std::cout << "overload" << std::endl;
}


then the call is resolved just fine, which means it has to do with the fact that X::type is a template dependent name, but still cannot work out why it fails. Any help is much appreciated.

Answer

The the compiler meets the call:

f(MyClass(), int());

It finds the first version of f. The compiler has to deduce X and Y. It deduces X to MyClass and Y to int.

It also finds the second version of f. The compiler has to deduce X. It deduces X to MyClass. Since X::type is a depented name it replaces X with MyClass that has already deduced. Thus second argument becomes MyClass::type which happens to be an int.

Now you have two candidates:

f(MyClass, int)

and

f(MyClass, MyClass::type)

At this points the compiler throws its hands up in the air because the two candidates are equally viable. Thus, you get an ambiguous call error.