Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to understand how an overloaded function is chosen

I have the following program:

#include <iostream>

namespace detail {

    template <class T> char test(int T::*)
    {
       std::cout << "Came to one\n";
       return 0;
    }

    template <class T> int test(...)
    {
       std::cout << "Came to two\n";
       return 0;
    }
}

struct A {};

int main()
{
   detail::test<A>(0);
   detail::test<int>(0);
}

When tested with g++ 4.8.2, it produces the following output:

Came to one
Came to two

My question: why is the first version of detail::test unambiguously chosen for the first call?

Update

In the absence of the first version of details::test, the code from main compiles fine. When it is there, the compiler thinks it's a better match for detail::test<A>() than the second one.

Update 2

After reading about a pointer to member is well-formed even for incomplete types or without members of the designated type., I tried the following and it works.

struct A;

int main()
{
   detail::test<A>(0);
   detail::test<int>(0);
}

The C++11 standard has quite a few places to uncover concepts that I wouldn't have thought of.

like image 671
R Sahu Avatar asked Dec 15 '22 21:12

R Sahu


2 Answers

The compiler goes through the Holy Trinity of Name Lookup, Argument Deduction and Overload Resolution. Name lookup finds two overloads for test, and the argument deduction of a pointer to member will fail for a non-class type, but not for incomplete types or missing members. Finally, out of the viable candidates, overload resolution picks the best match (the ellipsis conversion being the lowest rank).


There are three relevant Standard quotes here:

According to the Example in 8.3.3 Pointers to members [dcl.mptr]/2 a pointer to member is well-formed even for incomplete types or without members of the designated type.

According to 14.8.2 Template argument deduction [temp.deduct]/8:

If a substitution results in an invalid type or expression, type deduction fails.

One of the many examples listed is:

Attempting to create “pointer to member of T” when T is not a class type.

Finally, according to 13.3.3.2 Ranking implicit conversion sequences [over.ics.rank] the ellipsis (...) overload has the lowest rank of all implicit conversion sequences during overload resolution:

2 When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)

— a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and

— a user-defined conversion sequence (13.3.3.1.2) is a better conversion sequence than an ellipsis conversion sequence (13.3.3.1.3).

Your first call detail::test<A>(0); has two viable candidates, but it picks the first overload because it is a better match . The second call detail::test<int>(0); gives a substition error on the first overload and therefore picks the second match.

like image 173
TemplateRex Avatar answered Feb 06 '23 16:02

TemplateRex


In this instance, the first overload gets picked over the second because it is a better fit as far as the parameter conversion sequences are concerned - ellipsis conversion sequences rank last.

like image 39
Pradhan Avatar answered Feb 06 '23 14:02

Pradhan