当我使用g++
template<class T>
class A
{};
template<class T>
class B
{
public:
typedef A<T> A;
};
编译器告诉我
error: declaration of ‘typedef class A<T> B<T>::A’
error: changes meaning of ‘A’ from ‘class A<T>’
另一方面,如果我将typedef
更改为
typedef ::A<T> A;
用g++
编译好的一切都很好。 Clang ++ 3.1并不关心。
为什么会这样?并且是第二个行为标准吗?
答案 0 :(得分:10)
g ++是正确的,符合标准。从[3.3.7 / 1]开始:
类S中使用的名称N应引用其中的相同声明 上下文,并在完成范围内重新评估 违反此规则需要诊断。
在typedef之前,A
引用了::A
,但是通过使用typedef,你现在让A
引用被禁止的typedef。但是,由于no diagnostic is required
,clang也符合标准。
jogojapan's comment解释了此规则的原因。 对您的代码进行以下更改:
template<class T>
class A
{};
template<class T>
class B
{
public:
A a; // <-- What "A" is this referring to?
typedef A<T> A;
};
由于类范围的工作原理,A a;
变得模棱两可。
答案 1 :(得分:1)
我将补充Jesse关于GCC在编译中看似奇特的行为的答案:
typedef A<T> A;
VS
typedef ::A<T> A;
这也适用于使用以下形式的语句:
using A = A<T>;
using A = ::A<T>;
GCC中似乎发生的事情是,在编译声明B :: A的typedef / using语句期间,符号B :: A成为using语句本身的有效候选 。即在说using A = A<T>;
或typedef A<T> A;
时,GCC会同时考虑::A
和B::A
A<T>
的有效候选人。
这似乎很奇怪,因为正如你的问题暗示的那样,你不希望新的别名A成为typedef本身的有效候选者,但正如Jesse的回答所说,在一个类中声明的任何内容都可以被其他内容看到这个类 - 在这种情况下显然甚至是声明本身。这种行为可以通过这种方式实现,以允许递归类型定义。
您找到的解决方案是为GCC精确指定您在typedef中引用的A,然后它不再抱怨。