与类型同名的变量 - 哪个编译器是正确的

Variable with same name as type - which compiler is right?

本文关键字:编译器 类型 变量      更新时间:2023-10-16

在这段代码中:

typedef int foo;
struct S
{
  foo foo;
};
int main() {}

所有版本的clang -std=c++14都接受此代码,但所有版本的g++ -std=c++14报告:

5 : error: declaration of 'foo S::foo' [-fpermissive]
foo foo;
^
1 : error: changes meaning of 'foo' from 'typedef int foo' [-fpermissive]

代码正确吗?

代码错误。 typedef是现有类型的新名称。因此,您不能创建具有类型名称的变量,例如foo foo;等于int int

g++ -std=c++14是正确的。

另请参阅此问题

根据C++标准,声明与类型同名的变量通常是正确的代码,但在类定义中是无效的代码。类大小写是特定的,因为在类定义中声明的名称在整个类定义中可见,在该名称的声明点之前和之后。在其他作用域(全局、命名空间、函数等(中,声明的名称仅在声明点之后可见。

对于问题中给出的示例:如果您在成员声明之前有另一个对foo的引用 foo foo;

struct S { foo another_member; foo foo; };

它应该指的是哪个foo?到类型或成员foo?在这种情况下,可以从上下文中推断出类型的含义,但C++标准可能避免了处理极端情况的复杂性。

但是foo foo;类定义之外的有效代码:除了 Serge Ballesta 在他的回答中已经引用的 [dcl.spec] 节摘录之外,[dcl.decl] 节在这种情况下很有用。它为这种情况提供了一个有效的例子(在文本下方注释中的C++标准文本的旧版本中,在更高版本中作为正文的一部分( - 这里来自 N3242 草案(C++11 的最终草案(:

具有多个声明符的声明通常等效于相应的声明序列,每个声明都有一个声明符。那是

T D1, D2, ... Dn;

通常等价

T D1; T D2; ... T Dn;

其中T是一个 decl-specifier-seq,每个Di都是一个 init-declarator。当由其中一个声明符引入的名称时,会发生异常隐藏 decl 说明符使用的类型名称,以便当相同的Decl 说明符用于后续声明中,它们没有含义相同,如

struct S ... ;

S S, T; // declare two instances of struct S

这不等同于

struct S ... ;

S S;

S T; // error

[dcl.spec] 节的摘录也很有用,因为它描述了当变量声明中的名称被解释为类型名称以及当被解释为变量名称时(Serge Ballesta 的答案中给出了长引号(:

。它被解释为 decl-specifier-seq 的一部分,当且仅当...

原始问题中给出的类案例的相关部分是 [basic.scope.class]:

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

这意味着原始问题的代码无效,但编译器不需要给出错误。因此,clang 和 gcc 的行为都正确(根据 C++ 标准(。

我会说CLang在这里是正确的 - 即使我永远不会使用它。

C++ 14 草案 N4296 在 7.1 说明符 [dcl.spec] 3 中说

如果在解析 decl-specifier-seq 时遇到类型名称,则会将其解释为 decl-specifier- 的一部分 seq 当且仅当 decl-specifier-seq 中除了 cv 限定符之外没有以前的类型说明符。这 顺序应自洽,如下所述。[ 示例:

typedef char* Pc;
静态电脑; 错误:缺少名称

在这里,声明静态 Pc 格式不正确,因为没有为 Pc 类型的静态变量指定名称。

要获得一个名为 PC 的变量,必须存在一个类型说明符(除了 const 或 volatile(以指示 typedef-name Pc 是被(重新(声明的名称,而不是 decl-specifier 序列的一部分。

再举一个例子,

void f(const Pc(;//void f(char* const( (not const char*(
void g(const int Pc(;//void g(const int(

(强调我的(

即使一个例子不是规范性的,它也让我们认为对于C++规范的编写者来说,变量可以重新声明 typedef 的名称。

但是 g++ 只是更保守,看起来更合理。如果我在生产代码中看到这样的结构,程序员很快就会学会不再这样做,即使是我编译器也接受了它......

相关文章: