brahmin brahmin - 1 year ago 195
C++ Question

c++ templates and ambiguous call to overloaded function

I'm stuck with the following problem. I was using Xcode 7 and had no issue with my project. After trying to compile it on Visual Studio Express 2015, I get the message

"Error C2668 ambiguous call to overloaded function"
in the code. I couldn't find anything specific to visual studio related to the problem.

I made a minimal working example supposed to be used on VS (there's no error on Xcode). The weird part involves the
part. It's as if VS compiler couldn't auto deduce more types and/or arguments than a limit.

Update :

A workaround was proposed by Sam Varshavchik consisting in using partial specialization with an intermediate template class. This is a solution that I would like to avoid. First because it's not convenient in the context it would apply in my code, second because this compilation error is unclear to me. This error doesn't show up in Xcode7, and func2 has no error even in VS. Despite the explanation of WhiZTiM to which I agree with, fact is, overloading in this context can sometimes work, and sometimes not. And I'd really like to know why.

Update 2 :

According to bogdan, this is probably a bug in GCC and MSVC. I'm going to try to report it. (I love so much this first week with visual studio)

Update 3 :

Bug reported at

functions.h :

template <class T>
class BX {
public :
BX() {}
~BX() {}

template <class T1, class T2>
class G {
public :
G() {}
~G() {}

template <template <class T> class T1, class T>
class DT {};

class B {
public :
//I want func to work
template <template <class T> class T1, class T, class M>
static void func(const M& m, const DT<T1, T>* dt, T1<T>& val) {}

template <template <class T> class T1, class T, class M>
static void func(const G<T1<T>, M>& g, const DT<T1, T>* dt, T1<T>& val) {}

//here is a small variation of func as a test
template <template <class T> class T1, class T, class M>
static void func2(const M& m, const DT<T1, T>* dt) {}

template <template <class T> class T1, class T, class M>
static void func2(const G<T1<T>, M>& g, const DT<T1, T>* dt) {}


int main() {
BX< int > bx;
G<BX< int >, int> g;
DT<BX, int>* dt;
B::func(g, dt, bx);//Error C2668 'B::func': ambiguous call to overloaded function
B::func2(g, dt);//no error

Answer Source

This looks like a bug in MSVC and GCC. The call should resolve to the second overload (Clang and EDG are doing that).

For the call B::func(g, dt, bx), name lookup finds the two func templates. Template argument deduction and substitution is performed on each of them in order to generate function template specialization declarations that can subsequently participate in overload resolution. Deduction succeeds for both templates and we're left with two specializations:

void B::func<BX, int, G<BX<int>, int>>(const G<BX<int>, int>)&, const DT<BX, int>*, BX<int>&);
void B::func<BX, int, int>            (const G<BX<int>, int>)&, const DT<BX, int>*, BX<int>&);

The two functions have identical parameter declaration clauses, so clearly overload resolution cannot distinguish between them based on conversions from the arguments of the call; it has to resort to the last two steps in the process.

First, if one of the functions is a template specialization and the other one is not, the non-template one is preferred; not applicable here.

Last, it looks at the templates from which the two specialization declarations were synthesized; if one of the templates is more specialized than the other according to the partial ordering of function templates, then the corresponding specialization is preferred. (This is the only place in the process where the original templates come back into play.)

The description below is not very accurate and skips quite a few details, but I'm trying to concentrate on the parts that are relevant to this case. Very roughly:

  • First, references and cv-qualifiers are stripped from the function parameter declarations of both templates, yielding:

    F1(M          , const DT<T1, T>*, T1<T>)
    F2(G<T2<U>, V>, const DT<T2, U>*, T2<U>)

    (template parameter names changed to avoid confusion)

  • Then, deduction is attempted as if for a call to one template using the forms of the function parameters of the other template as arguments, and then the other way around. In this case, the last two pairs of corresponding parameters have identical forms, so deduction succeeds both ways. For the first pair of corresponding parameters:

    • Deducing M from an argument of the form G<T2<U>, V> works; M is deduced as G<T2<U>, V>.
    • Deducing T2, U and V in G<T2<U>, V> from an argument of the form M doesn't work (M can be anything).

    In other words, G<T2<U>, V> is a "more specific" form than M; it cannot represent all the types that M can represent; this is the intuitive meaning that more specialized is trying to formalize in this context.

  • So, deduction works for all pairs of corresponding parameters from F2 to F1, but not the other way around. This makes F2 more specialized than F1 in terms of partial ordering.

This means that the specialization corresponding to the second template is preferred in overload resolution.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download