Search code examples
c++templateslanguage-lawyername-lookup

Does the use a simple-template-id in a nested-name-specifier unambiguously mean a class template specialization?


struct A{
    template<typename U>
    void T(){}
};
struct B{
    template<typename U>
    struct T{
       using type = U;
    };
};
struct C:A,B{

};
int main(){
    C::T<int>::type d;
}

This example is accepted by neither GCC nor Clang.

As per basic.lookup.qual#1

The name of a class or namespace member or enumerator can be referred to after the​::​ scope resolution operator ([expr.prim.id.qual]) applied to a nested-name-specifier that denotes its class, namespace, or enumeration. If a​::​ scope resolution operator in a nested-name-specifier is not preceded by a decltype-specifier, lookup of the name preceding that ​::​ considers only namespaces, types, and templates whose specializations are types.

That means that when looking up the declarations for the template name T, the specialization of T shall denote a type in this context. On the other hand, as per class.member.lookup#4

If C contains a declaration of the name f, the declaration set contains every declaration of f declared in C that satisfies the requirements of the language construct in which the lookup occurs.

Again, when looking up the template T in the scope of C, only those templates whose specialization is a type should be considered by this lookup. The scope of C does not have any declarations for T, hence the lookup will be performed for S(T,C) in every one of its base classes. The template T in A does not satisfy the requirement. Meanwhile, the template T declared in the scope of B does satisfy the requirement. So the lookup is not ambiguous and the B::T is the unique result. That means C::T<int>::type d should be well-formed. Why do both GCC and Clang reject this example? Can it be considered a bug of in both? If I missed something, what's the reason that this example should be ill-formed?


Solution

  • The lookahead required to have the lookup for T depend on the :: even as the interpretation of < depends on the meaning of T is considered undesirable. As such, lookup for a name followed by < is not restricted to types and namespaces regardless of any >:: found later. P1787R6 fixed this, restricting the special lookup to identifiers immediately followed by :: (since other kinds of names can’t refer to types or namespaces anyway).