与类型同名的变量 - 哪个编译器是正确的
Variable with same name as type - which compiler is right?
在这段代码中:
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++ 只是更保守,看起来更合理。如果我在生产代码中看到这样的结构,程序员很快就会学会不再这样做,即使是我编译器也接受了它......
- 我收到同义重复编译器错误。我应该如何修复"类型"X"的参数与类型"X"的参数不兼容?
- 从类型列表中递归删除重复项会导致编译器堆空间错误 (VS2017)
- 如果可推导类型上有替换,可变参数模板类型推导会使编译器崩溃
- 在模板化成员函数的返回类型中使用 std::enable_if 时的编译器差异
- 使用typedef'ed返回类型声明友元函数时出现编译器错误
- 当类型适当的构造函数可用时,为什么一个编译器尝试使用已删除的副本构造函数
- 需要有关运行具有输入类型的编译器的帮助
- 编译器给出错误:format 指定类型 'float *',但参数的类型'double' [-Wformat]
- 如何告诉编译器名称是模板类型名称?
- 不同c++编译器之间的自动类型推导不匹配
- 编译器错误"在if语句中分配不兼容的类型"
- 如何在编译器时动态获取元组内的变量类型C++?
- 如果类在 C++ 中具有常量或引用类型的非静态数据成员,为什么编译器不提供默认赋值运算符?
- 如果条件取决于模板类型并且在编译时已知,是否可以保证C++编译器不会生成分支?
- 如何配置VS 2012的编译器来更改基本数据类型大小?
- 交叉编译器树莓派不完整类型,而本机编译器工作
- 为什么编译器不能从返回类型中推断出模板参数?
- G++ 编译器无法编译完全定义的类型
- 为什么编译器需要指定类型?
- 如果类包含基类类型的成员作为第一个元素,后跟其他成员,编译器是否可以优化空基?