【问题标题】:Use of typename keyword with typedef and new在 typedef 和 new 中使用 typename 关键字
【发布时间】:2011-05-24 04:57:48
【问题描述】:

考虑这段代码,

template<class T>
struct Sample
{ 
     typename T::X *x; //declare pointer to T's X
};

在上面的代码中,关键字typename是编译器需要的,这样可以消除模板中嵌套类型和嵌套值的歧义。这意味着,在没有 typename 关键字的情况下,编译器会将其解释为 T::X 与 x 的乘积,

T::X *x; //multiply T::X with x

所以在这种可能出现歧义的情况下,关键字typename变成必要性,以消除歧义。但是,上下文本身消除歧义的情况很少。 other topic 讨论了基类和函数参数的上下文(尽管后者并没有消除歧义)。在这个话题中,我特别想讨论其他两个似乎明确,但我们仍然需要写typename

typedef typename T::X xtype;
pX = new typename T::X;  

在这两种情况下,关键字typedefnew 足以让编译器清楚地知道后面的内容是type不是 value。

所以我的问题是,为什么编译器仍然需要 typename 关键字,即使在明确的情况下,例如我们使用 typedefnew 时?


编辑(阅读Johannes Schaub的回复后):

//typedef NOT followed by a type!
int typedef A;

这种语法要求我稍微修改一下我的问题,以便其他人可以看到我试图提出的观点。

考虑一下,

T::X typedef *x;

所以从上下文来看,编译器仍然很清楚 T::X 是一种类型,无论它出现在 before typedef 还是 after typedef除非 C++ 允许我们编写 typedef 5 fivetypedef T::value t_value(其中 T::value 是 value),typedef 本身的存在消除了所有歧义,因此,typename 似乎是标准的不必要要求(在这种情况下)。同样的论点也适用于new


另外,我编写了一个类模板,它使用这个结构作为模板参数:

struct A 
{
        struct X { string name; };
        static const int X = 100;
};

我特别想知道下面的代码(来自构造函数)是否正确(可移植),

//two interesting statements
 pX = new typename T::X; //T::X means struct X
 product = T::X * p; //but here, T::X means int X

完整的代码是 here at ideone。回复前请看一下。 :-)

【问题讨论】:

  • 他的问题是为什么编译器在typedef 之后需要一个typename 关键字。因为在typedef 之后,它总是知道 typeString 是 Commin。但是我认为这就是语法分析器的编写方式。
  • @Pawel :问题是:为什么编译器在明确的情况下仍然需要 typename 关键字?
  • 这是编写语法的规则。真的需要从中做出意义吗?即使在 typedef 之后出现的也应该是一种类型。所以你必须键入它。编译器在 typedef 获得类型之后永远不知道。相反,它期望下一个标记是一个类型。并且 typename 证实了这一点。
  • @user256007 :你的理由对我来说并不令人信服,因为如果在 typedef 编译器之后没有找到类型,那么它会给出编译错误,就像你也使用 typename 一样。跨度>
  • @Nawaz:显然编译器不需要它。看看 MSVC! :)

标签: c++ templates typedef language-lawyer typename


【解决方案1】:

C++ 语法比这更疯狂。

// typedef NOT followed by a type!
int typedef A;

// new NOT followed by a type!
new (0) int;

其他人对您的示例发表了评论。 typename 说明符不会产生忽略非类型名称的查找。因此,如果您说new typename T::X,并且在T 中有一个对象名称X,仍然会找到它而不是类型名称X(但是GCC 在查找a 之后的名称时会忽略非类型名称typename。但这不符合标准)。


修改答案:

考虑一下,

T::X typedef *x;

所以从上下文来看,编译器仍然很清楚 T::X 是一个类型,无论它出现在 typedef 之前还是 typedef 之后。

编译器必须知道何时声明说明符和(即“类型部分”以及声明符部分何时开始(即“名称”部分)。有些声明类型部分为空:

// constructor definitions don't need a type section
MyClass::MyClass() { }

// conversion function definitions don't need a type section
MyClass::operator int() { }

如果您指定的名字不是类型,则类型部分结束,名称部分开始。说T::X 告诉编译器:

现在我想定义T::X

它从左到右读取,所以当它遇到typedef 时会认为你忘记了分号。在类内部,解释略有不同,但也很相似。这是一个简单而有效的解析。

同样的论点也适用于 new。

我倾向于同意你的观点。从语法上讲,它应该是明确的 if 你去掉括号。由于我从未编写过 C++ 解析器,因此可能存在我没有看到的隐藏陷阱。

在像new 这样的语言的极端情况下,每次添加typename 都可能需要编译器和标准编写者进行大量设计,而在绝大多数其他需要它的情况下仍需要typename .我不认为这是有回报的。

【讨论】:

  • 感谢这篇有价值的帖子。至少今天我学会了 typedef 的这种新语法。但我认为,它仍然不能令人满意地回答我的问题。请阅读我帖子中已编辑的部分!
【解决方案2】:

那为什么编译器还需要 typename 关键字呢?

因为语言的规则是这样制定的:在类型上下文中使用的每个从属名称必须以typename 开头(比照,模板名称也是如此和template 关键字)。现在,为什么语言规则在需要类型名来消除歧义的情况和上下文提供足够信息的情况之间没有区别?可能 - 做出决定时我不在场 - 使语言描述更加复杂(考虑丢失案例的后果,一种或另一种方式)。

在您的示例中,X 不是类型名称(如果与 C 兼容,其中标签名称不是自动类型名称,则可能),因此您需要使用 struct

pX = new struct T::X;

【讨论】:

    【解决方案3】:

    您的代码似乎进入了一个非常灰色的区域。

    这一段关于名字隐藏

    类名 (9.1) 或枚举名 (7.2) 可以隐藏一个名字 变量、数据成员、函数或 在同一范围内声明的枚举器。 如果一个类或枚举名称和一个 变量、数据成员、函数或 枚举器被声明在同一个 范围(以任何顺序)具有相同的 名称,类或枚举名称是 隐藏在变量、数据的任何地方 成员、函数或枚举数名称 可见。

    似乎表明编译器抱怨A::X 不是类型名称是正确的。

    【讨论】:

    • 这并不令人信服,因为无论编译器可以从 typename 的使用中获得什么提示(在这两种情况下),编译器甚至可以从 typedef 和 @987654324 的使用中获得相同的提示@.
    • @Nawaz:我更关心名称隐藏。 struct X {}; int X; typedef X Y; - 编译器只看到名为 X 的整数。不过,AProgrammer 正在做某事,因为 typedef struct X Y; “取消隐藏”了它。
    • 即使在这种情况下typedefnew 也可以扮演您期望typename 扮演的相同角色。 typename的思想是给编译器一个提示,后面的都是type,那么这个提示编译器在解析关键字typedef或者new的时候就已经可以得到了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-31
    • 1970-01-01
    • 1970-01-01
    • 2011-01-02
    • 1970-01-01
    相关资源
    最近更新 更多