4

Consider the code below. Although both overloads of fun accept pointers, passing nullptr to fun does not result in any compilation error. Whereas, the very similar function bun fails to compile. When I print the the types of the argument i using typeid(i).name() (after modifying the code just to get this printed) I get the same type, simply int*. What is the rule that resolves the ambiguity in fun case, but fails for bun? Thanks in advance!

#include <iostream>

struct Foo {
    int sth;
};

template< class U>
void fun(decltype(U::sth)* i){
    std::cout << "1" << std::endl;
}

template< class U>
void fun(U* i){
    std::cout << "2" << std::endl;
}

void bun(decltype(Foo::sth)* i){
    std::cout << "3" << std::endl;
}

void bun(Foo* i){
    std::cout << "4" << std::endl;
}

int main ( )
{
    fun<Foo>(nullptr);
    // bun(nullptr); --> call of overloaded 'bun(std::nullptr_t)' is ambiguous        
    return 0;          
}
-----------------------
output : 1
ozlsn
  • 144
  • 4
  • I have an inkling feeling now that it has to do with non deduced contexts, but I might be entirely wrong. – user975989 Feb 22 '19 at 03:53
  • The compiler can prove that the first template is more specialized than the second template, so it will always pick it when a template invocation can match both templates. That's the rule. Maybe if someone can't rattle off a capsule explanation of specialization that applies to this question precisely, I might get to this tomorrow. This is very thick in the weeds, and, unfortunately, I've lost a bookmark to a pretty good article that fully explains this, that I can just shamelessly plagiarize (not). – Sam Varshavchik Feb 22 '19 at 04:14

1 Answers1

1

Well, in fact, GCC accepts your code, but Clang does not. So it is not, at first, obvious whether the call is ambiguous.

You ask what rule resolves the ambiguity in the fun case; GCC evidently thinks that there is such a rule. I imagine the rule that GCC is applying is the rule [over.match.best]/1.7 that prefers a more specialized function template over a less specialized one.

The procedure for determining which function template is more specialized than the other is described in [temp.func.order] and is explained thoroughly in this SO answer. However, you will notice that when attempting to apply this procedure to the two overloads of fun as in this question, we run into the problem that the unique synthesized type that needs to be substituted for U in the first overload would need to have a member named sth, and the nature of this member is not specified, and although it may be clear to a human that deduction in the second fun overload must succeed regardless of what sth's type is, the compiler may not be able to prove it.

This is CWG 1157. As this issue is still open with no proposed resolution, I have no insight into whether WG21 intends for this overload resolution to succeed or not.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312