Duncan
Duncan

Reputation: 990

Name lookup on template base classes in non-dependent name scenarios

Take the following code as an example:

template <typename T>
struct foo_base
{
    void doit(T*) {}
};

template <typename T>
struct foo : foo_base<T> {};

template <typename T>
struct get_result
{
    template <typename Result>
    static Result GetType(void (T::*)(Result*));

    using type = decltype(GetType(&T::doit));
};

int main()
{
    std::cout << typeid(typename get_result<foo<int>>::type).name() << '\n';
}

This code will fail to compile with both GCC and Clang, but succeeds with MSVC. The error clang gives is:

<source>:21:27: error: use of undeclared identifier 'GetType'
    using type = decltype(GetType(&T::doit));
                          ^
<source>:26:34: note: in instantiation of template class 'get_result<foo<int> >' requested here
    std::cout << typeid(typename get_result<foo<int>>::type).name() << '\n';
                                 ^
<source>:19:19: note: must qualify identifier to find this declaration in dependent base class
    static Result GetType(void (T::*)(Result*));
                  ^

Typically I'd side with GCC/Clang when it comes to conformance, particularly when they both agree, but I can't exactly reason about why. When get_result<foo<int>> is instantiated, it should also instantiate foo_base<int>, so I would think that the expression T::doit should compile without issues.

FWIW the workaround is rather trivial:

template <typename Type, typename Result>
static Result GetType(void (Type::*)(Result*));

Upvotes: 2

Views: 459

Answers (1)

Jarod42
Jarod42

Reputation: 218323

&foo<int>::doit is actually &foo_base<int>::doit

so its type is void (foo_base<int>::*)(int*), but GetType expects argument of type void (foo<int>::*)(T*) and so cannot deduce T.

Upvotes: 1

Related Questions