Name conflict between namespace and class template: different compiler behavior

Viewed 565

Different compilers show different behavior compiling the following code:

namespace N
{
    namespace Foo
    {
        template <typename>
        struct Foo
        {
        };
    }
}

template <typename Type>
using Foo = N::Foo::Foo<Type>;

namespace N
{
    template <typename Type>
    struct Bar : Foo<Type>
    {
    };
}


int main()
{
}

Compilers tested and their compilation flags:

  • clang++ 5.0.0: -std=c++14 -Wall -Wextra -Werror -pedantic-errors
  • g++ 7.2: -std=c++14 -Wall -Wextra -Werror -pedantic-errors
  • vc++ 19.10.25017 (VS 2017): /EHsc /Za /std:c++14 /permissive-
  • icc 18.0.0: -std=c++14 -Wall -Werror

Results of the compilation:

  • clang++:

    18 : <source>:18:15: error: expected class name
            struct Bar : Foo
                         ^
    
  • g++: successful compilation

  • vc++:

    18 : <source>(18): error C2516: 'Foo': is not a legal base class
    13 : <source>(13): note: see declaration of 'Foo'
    20 : <source>(20): note: see reference to class template instantiation 'N::Bar<Type>' being compiled
    18 : <source>(18): error C2143: syntax error: missing ',' before '<'
    
  • icc: successful compilation

Which compiler behavior is standard compliant?

Additional information:

2 Answers

The spec says

During the lookup for a base class name, non-type names are ignored ([basic.scope.hiding])

The name is Foo<Type>, it's a type name. And the name N::Foo is not a type name, so it must be ignored. In similar situations where certain names are ignored, the wording is more explicit though

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

Here, it doesn't only say "type names" or "non-type names" when it wants to allow type-template<arguments>. But it specifically says "templates whose specializations are types". I think this confusion is the reason why there's implementation divergence here. The name Foo<Type> is what I would call a "composite name", because it consists of nested names inside of it. So it may be unclear which exact names in it are to be ignored and which not.

Related