20

当我编译以下代码片段时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 不关心任何一种方式。

为什么会这样?第二个行为标准是什么?

4

2 回答 2

13

g++ 是正确的并且符合标准。从 [3.3.7/1] 开始:

在 S 类中使用的名称 N 应在其上下文中引用相同的声明,并且在 S 的完整范围内重新评估时。违反此规则不需要诊断。

在 typedef 之前,A引用了::A,但是通过使用 typedef,您现在A可以引用被禁止的 typedef。但是,因为no diagnostic is required,clang 也符合标准。

jogojapan 的评论解释了这条规则的原因。对您的代码进行以下更改:

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;变得模棱两可。

于 2012-08-29T23:07:21.813 回答
2

我将在 Jesse 的回答中添加关于 GCC 在编译中看似特殊的行为:

typedef A<T> A;

对比

typedef ::A<T> A;

这也适用于以下形式的 using 语句:

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 考虑两个::AB::A有效的候选人时A<T>

这似乎很奇怪,因为正如您的问题所暗示的那样,您不希望新别名 A 成为 typedef 本身内的有效候选者,但正如 Jesse 的回答也所说,在类中声明的任何内容都对类中的其他所有内容可见 -在这种情况下,显然甚至是声明本身。这种类型的行为可以以这种方式实现以允许递归类型定义。

您找到的解决方案是在 typedef 中准确地为 GCC 指定您所指的 A ,然后它不再抱怨。

于 2014-01-30T18:47:39.327 回答