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

9
不同的编译器在编译以下代码时表现不同:(点此查看)
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

编译结果:

  • 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

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

附加信息:

2个回答

5

规范说明:

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

名称 Foo<Type> 是一个类型名称。而名称 N::Foo 不是类型名称,因此必须被忽略。在类似情况下,当要忽略某些名称时,措辞通常更为明确。

如果嵌套名称限定符中的​::​作用域解析运算符没有被decltype-specifier所预先指定,则在该​::​之前的名称的查找仅考虑命名空间、类型和其特化为类型的模板

在这里,当允许 type-template<arguments> 时,它不仅说"type names""non-type names"。而是明确说明"templates whose specializations are types"。我认为这种混淆是导致实现分歧的原因。名称 Foo<Type> 是我所谓的"复合名称",因为它由其中的嵌套名称组成。因此,可能不清楚要忽略其中的哪些精确名称和哪些不忽略。


更准确地说:clang++vc++在名称解析机制的实现上存在缺陷,而g++icc在这个领域是符合标准的。我的理解正确吗? - Constructor
那么没有模板的相同代码呢? clang ++ 在这种情况下也有错误吗? - Constructor
@Constructor 我不知道。标准在这里并不十分清楚。我建议向std-discussion发起投诉。没有模板,clang显然存在一个错误。 - Johannes Schaub - litb

1

在查找 Foo<Type> 之前,会先查找名称为 Foo 的内容。由于查找的并非基类名,因此 [class.derived]/2 中的查找规则不适用于对 Foo 的查找。

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

一个类X的定义中,在成员函数体外、默认参数、noexcept-specifier、非静态数据成员的brace-or-equal-initializer或嵌套类定义25中使用的名称,应该以以下方式之一声明:
  • 在类X中使用之前声明它或是X的基类的成员([class.member.lookup]),或者
  • 如果X是类Y的嵌套类,在Y中定义X之前声明它,或者是Y的基类的成员(此查找依次适用于Y的封闭类,从最内层的封闭类开始),或者
  • 如果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,这导致程序不合法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接