命名空间和类模板之间的名称冲突:不同的编译器行为

时间:2017-11-25 19:46:39

标签: c++ templates namespaces c++14 language-lawyer

不同的编译器show different behavior编译以下代码:

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()
{
}

编译器及其编译标志:

  • 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

汇编结果:

  • 铛++

    18 : <source>:18:15: error: expected class name
            struct Bar : Foo
                         ^
    
  • g ++ :成功编译

  • 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 :成功编译

哪种编译器行为符合标准?

其他信息:

2 个答案:

答案 0 :(得分:5)

规范说

  

在查找基类名称时,将忽略非类型名称([basic.scope.hiding])

名称为Foo<Type>,它是类型名称。名称N::Foo不是类型名称,因此必须将其忽略。在忽略某些名称的类似情况下,措辞更明确,但

  

如果嵌套名称说明符中的:: scope resolution运算符前面没有decltype-specifier,则查找此前面的名称::仅考虑其特化类型为类型的名称空间,类型和模板

在此,它不会只说&#34;类型名称&#34; &#34;非类型名称&#34; 想要允许type-template<arguments>。但它具体说明了&#34;模板的专业化类型&#34; 。我认为这种混淆是导致这里实现分歧的原因。名称Foo<Type>就是我所说的&#34;复合名称&#34;,因为它由内部的嵌套名称组成。因此,可能不清楚中哪些的确切名称将被忽略,哪些不被忽略。

答案 1 :(得分:1)

在查找Foo之前查找名称Foo<Type>Foo的查找不是基类名称的查找,因此[class.derived] / 2中的查找规则不适用。

参考[basic.lookup.unqual] / 7:

  

在成员函数体外定义类X的名称,非静态数据成员的默认参数,noexcept-specifier,brace-or-equal-initializer或嵌套类定义25应在以下方式之一:

     
      
  • 在X类中使用之前或者是X([class.member.lookup])基类的成员,或者

  •   
  • 如果X是Y类的嵌套类,在Y中定义X之前,或者应该是Y的基类的成员(此查找依次应用于Y的封闭类,从最内层开始封闭类),26或

  •   
  • 如果X是本地类或是本地类的嵌套类,则在包含X类定义的块中定义类X之前,或

  •   
  • 如果X是名称空间N的成员,或者是作为N成员的类的嵌套类,或者是作为成员的函数的本地类中的本地类或嵌套类N,在命名空间N中的类X的定义之前或在N的封闭命名空间之一中。

  •   
     

[实施例:

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
  

- 结束示例]

在全局命名空间之前考虑名称空间N,因此首先找到N::foo,这会导致程序格式错误。