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

问题描述 投票:0回答:3

在此代码中:

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]

代码正确吗?

c++ compiler-errors clang language-lawyer variable-names
3个回答
7
投票

代码错误。

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

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

另请参阅此问题


4
投票

根据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-声明符。 当声明者之一引入名称时会发生异常 隐藏 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 的一部分当且仅当 ...

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

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

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


1
投票

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

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

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

typedef char* PC;
静态PC; // 错误:名称缺失

此处,声明 static Pc 格式错误,因为没有为 Pc 类型的静态变量指定名称。

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

再举个例子,

void f(const Pc); // void f(char* const) (不是 const char*)
void g(const int Pc); // void g(const int)

(强调我的)

即使示例不规范,它也让人认为对于 C++ 规范的编写者来说,变量可以重新声明 typedef 的名称。

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

© www.soinside.com 2019 - 2024. All rights reserved.