Reputation: 7473
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:
-std=c++14 -Wall -Wextra -Werror -pedantic-errors
-std=c++14 -Wall -Wextra -Werror -pedantic-errors
/EHsc /Za /std:c++14 /permissive-
-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:
Upvotes: 9
Views: 693
Reputation: 13040
The name Foo
is looked up before the lookup of Foo<Type>
. The lookup for Foo
is not a lookup for a base class name, so the lookup rule in [class.derived]/2 does not apply.
Refer to [basic.lookup.unqual]/7:
A name used in the definition of a class X outside of a member function body, default argument, noexcept-specifier, brace-or-equal-initializer of a non-static data member, or nested class definition25 shall be declared in one of the following ways:
before its use in class X or be a member of a base class of X ([class.member.lookup]), or
if X is a nested class of class Y, before the definition of X in Y, or shall be a member of a base class of Y (this lookup applies in turn to Y's enclosing classes, starting with the innermost enclosing class),26 or
if X is a local class or is a nested class of a local class, before the definition of class X in a block enclosing the definition of class X, or
if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the definition of class X in namespace N or in one of N's enclosing namespaces.
[ Example:
namespace M {
class B { };
}
namespace N {
class Y : public M::B {
class X {
int a[i];
};
};
}
// The following scopes are searched for a declaration of i:
// 1) scope of class N::Y::X, before the use of i
// 2) scope of class N::Y, before the definition of N::Y::X
// 3) scope of N::Y's base class M::B
// 4) scope of namespace N, before the definition of N::Y
// 5) global scope, before the definition of N
— end example ]
Namespace N
is considered before the global namespace, so N::foo
is found firstly, which causes the program ill-formed.
Upvotes: 1
Reputation: 506985
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.
Upvotes: 5