Constructor
Constructor

Reputation: 7473

Name conflict between namespace and class template: different compiler behavior

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:

Results of the compilation:

Which compiler behavior is standard compliant?

Additional information:

Upvotes: 9

Views: 693

Answers (2)

xskxzr
xskxzr

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

Johannes Schaub - litb
Johannes Schaub - litb

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

Related Questions