Home > Software design >  Different result for function resolution on MinGW64 and MSVC
Different result for function resolution on MinGW64 and MSVC

Time:11-15

template <typename T>
int get_num(const T&)
{
    return 42;
}

struct Foo
{
    int i = get_num(*this);
};

int get_num(const Foo&)
{
    return 23;
}

int main()
{
    std::cout << Foo().i << std::endl; // MinGW64 - 42, MSVC - 23
    return 0;
}

MSVC chooses non-template get_num "overload". It will successfully link even if the template is only forward-declared. MinGW64 will choose the default template implementation. Will fail to link if there is no definition for the template.

Who is right and who is wrong? Whad does the standard say?


However this version yields the same results for both compilers... Why does it not go to infinite recursion?

template <typename T>
int get_num(const T& t)
{
    std::cout << "Template version called" << std::endl;
    return get_num(t);
}

struct Foo
{
    int i = get_num(*this);
};

int get_num(const Foo&)
{
    std::cout << "Non-Template version called" << std::endl;
    return 23;
}

MSVC output:

Non-Template version called
23

MinGW64 Output:

Template version called
Non-Template version called
23

I think ADL is involved when using MSVC.

CodePudding user response:

For the first sample, MinGW is correct and MSVC is incorrect: the get_num call can only consider the function template.

This is a question of overload resolution, so I'll start in clause [over]. get_num(*this) is a plain function call expression, so [over.call.func] applies:

In unqualified function calls, the name is not qualified by an -> or . operator and has the more general form of a primary-expression. The name is looked up in the context of the function call following the normal rules for name lookup in function calls. The function declarations found by that lookup constitute the set of candidate functions.

"Name lookup" is discussed in section [basic.lookup]. Paragraph 2:

A name "looked up in the context of an expression" is looked up as an unqualified name in the scope where the expression is found.

One complication here is that the get_num(*this) default member initializer expression doesn't get used by anything until the default constructor of Foo is implicitly defined by its odr-use in main. But the lookup is determined from the code location of the expression itself, no matter that it's used in the process of that implicit definition.

For the second code sample, MinGW is again correct: the apparently recursive call inside the template definition actually calls the non-template function.

This is a result of "two phase lookup" for dependent function calls, described in section [temp.dep.res]. Briefly, since the type of t depends on a template parameter, the name get_num in the expression get_num(t) is considered a dependent name. So for each instantiation of the function template, it gets two ways of finding candidates for overload resolution: the ordinary immediate way which finds the get_num function template, plus another lookup from the point of instantiation. The specialization get_num<Foo> has point of instantiation right after the main() definition, so overload resolution is able to find the non-template from the instantiation context, and the non-template wins overload resolution.

(Argument-dependent lookup is a related tangent issue to this second point. ADL applies to declarations from the instantiation context but not declarations from the definition context. But it's not directly a reason for the behaviors in either example program.)

None of this has changed significantly between C 14 and the latest draft, as far as I can see.

  • Related