Search code examples
c++templatesc++11type-traits

Ambiguous call in custom member detector


I was working in my own implementation of a member detector to improve my programming skills. The following code compile well with g++ but clang++ reject the code, the error is :

error: call to 'check' is ambiguous main.cpp:19:17: note: candidate function [with U = has_member::HasIt]

static char check( decltype(U::i)* );

main.cpp:22:16: note: candidate function [with U = has_member::HasIt]

static int check(U*);

Here's the code of the class

template<typename T>
struct has_member 
{

    struct Fallback
    {
        int i;
    };

    struct HasIt : Fallback, T
    {};

    template<class U>
    static char check( decltype(U::i)* ); 

    template<typename U>
    static int check(U*);    

    static const bool value = sizeof(check<HasIt>( nullptr ) ) == sizeof(int);
};

class Test
{
public:

};

int main()
{
    auto v  = has_member<Test>::value;

    std::cout << std::boolalpha << v;
}

Live example here

The question is : is the code valid ? If it is why g++ accepts it ?


Solution

  • The code should be valid. We have two viable candidates:

    template <class U>
    static char check( decltype(U::i)* ); // --> int*
    
    template <class U>
    static int check( U* ); // --> HasIt*
    

    nullptr is convertible to both pointer types, neither conversion is better than the other, both candidates are function templates. But the former is more specialized than the latter, so it should be preferred. This is a clang bug.

    A simple workaround that works for both compilers is to change the second overload to instead be:

    template<typename U>
    static int check(...);   
    

    Since anything is preferred over ellipses, this still remains the fallback option, without having to rely on template partial ordering rules.

    Furthermore, since we're in C++11, we can just use the return types directly:

    template<class U>
    static std::false_type check( decltype(U::i)* ); 
    
    template<typename U>
    static std::true_type check(...);
    
    using type = decltype(check<HasIt>(nullptr));
    static constexpr bool value = type::value;