【问题标题】:Is the typedef-name optional in a typedef declaration?typedef 声明中的 typedef-name 是可选的吗?
【发布时间】:2011-09-17 23:58:08
【问题描述】:

当我看到以下代码在 g++-4.2 中编译时没有错误或警告时,我感到非常惊讶:

typedef enum test { one };

我的假设是,如果您使用 typedef 关键字,则需要一个额外的标识符,如下所示:

typedef enum test { one } test;

如前所述,g++-4.2 甚至在没有警告的情况下接受它。 Clang++ 3.0 警告“warning: typedef requires a name”,类似地 Comeau 警告“warning: declaration requires a typedef name”,g++-4.6 通知:“warning : 'typedef' 在此声明中被忽略"。

我无法确定标准中允许这样做的位置,而且我发现两个编译器警告它是必需,这有点令人困惑,如果它不应该是一个错误typedef-name 是必需的,但不存在?

更新:我使用相同的编译器检查了 C。 Clang 和 Comeau 产生相同的输出,gcc 给出警告:“warning: useless storage class specifier in empty declaration”,这似乎更令人困惑。

更新:我检查了删除枚举的名称,结果是一样的:

typedef enum { one };

与命名结构类似:

typedef struct named { int x };

但不是使用未命名的结构,在这种情况下,代码在 g++ (4.2/4.6) 中被拒绝,并显示“error: missing type-name in typedef-declaration”, gcc (4.2/4.6 ) 给出警告:“警告:未定义实例的未命名结构/联合”,clang++“警告:声明未声明任何内容”,comeau“错误:声明需要一个 typedef 名称"

【问题讨论】:

  • 有趣,VS2010 也接受代码,没有任何警告/错误。
  • 语法不是typedef enum { one } test吗?
  • 是的,我认为这应该是一个错误。
  • @Radu:这是未命名枚举的语法,而不是这里要问的内容。
  • @0A0D:这使它成为非 C++ 的原因是什么?如果不是typedef'd,我什至不需要枚举限定符。

标签: c++ c typedef


【解决方案1】:

我唯一能找到的是C++03标准§7.1.3 [dcl.typedef] p1中的以下内容:

类型定义名称:

  • 标识符

使用typedef 说明符声明的名称变为typedef-name

请注意 identifier 后面缺少的 opt,这表明,至少对我来说,需要一个 identifier对于 typedef-name。奇怪的是所有经过测试的编译器(默默地)都接受了这一点。


编辑:经过@Jonathan的回答,我在与上面相同的标准中找到了以下内容:

decl-specifier:

  • 存储类说明符
  • 类型说明符
  • 函数说明符
  • friend
  • typedef

可以看出,它为typedef 提供了一个额外的案例,storage-class-specifiers 上的列表证实了这一点:

存储类说明符:

  • auto
  • register
  • static
  • extern
  • mutable

所以,在 C++ 案例中,我们和以前一样一无所知。

【讨论】:

  • 据我所知,那里没有可选性,但我无法找到语法 typedef-name 的使用位置,以防万一可选性被标记在那里(类似于 typedef type-specifier typedef-name_opt
  • @David:是的,我也找不到类似的东西。 :/ 也许 C89 标准在这方面更详细。
  • 嗯? Clang、Comeau 和 g++4.6 都发出警告。这意味着他们没有“默默接受”它。 如果这是一个错误,那么他们就诊断出了错误,C++ 标准只要求符合标准的实现诊断格式错误的程序。它要求符合的实现拒绝编译它们,尽管它也没有定义它们的行为。一个符合标准的 C++ 编译器使用该语法产生的 C 含义是完全合法的,只要它发出警告。
  • @Steve:我把“静默”放在括号里,表示我的意思只是其中的一部分。
  • 啊,好的。那么我的初步解释是 g++ 4.2 是一个错误,对于其他人来说,因为它们都是 C 编译器,而且因为 C 的含义是无害的,所以他们的作者认为使用 C 的含义是合理的。如果您使用-Werror,那么您不必担心诊断和拒绝之间的区别:-)
【解决方案2】:

这是一种允许但没有任何好处的退化语法。大多数现代编译器都会被激怒发出警告。默认情况下,他们可能不会。没有 typedef 名称,关键字typedef 是多余的;在您的示例中,它完全等同于:

enum test { one };

另一个可能发生的地方是结构:

typedef struct SomeThing { int whatever; };

这相当于:

struct SomeThing { int whatever; };

请注意,typedef 正式(或语法上)是“存储类说明符”,例如 staticexternautoregister


C 标准

在 ISO/IEC 9899:1999(即 C 标准)中,我们发现:

§6.7 声明

语法

声明

声明说明符 init-declarator-listopt;

声明说明符

storage-class-specifier declaration-specifiersopt

类型说明符 声明说明符opt

类型限定符 声明说明符opt

函数说明符 声明说明符opt

初始化声明器列表

init-declarator

init-declarator-list , init-declarator

初始化声明器

声明符

声明符 = 初始化器

并且(根据要求):

§6.7.1 存储类说明符

语法

存储类说明符:

typedef

extern

static

auto

register

如果您跟踪该语法,则有很多退化的可能性,而您展示的只是众多可能性之一。


C++ 标准

C++ 可能有不同的规则。

在 ISO/IEC 14882:1998(原始 C++ 标准)中,我们在第 7.1.1 节“存储类说明符”中发现 C++ 不将 typedef 视为存储类;该列表添加了mutable 并排除了typedef。所以,C++中typedef的语法规范肯定和C规范不同。

§7 声明

声明指定如何解释名称。声明的格式为

声明序列:

声明

declaration-seq 声明

声明:

块声明

函数定义

模板声明

显式实例化

显式专业化

链接规范

命名空间定义

块声明:

简单声明

asm 定义

命名空间别名定义

使用声明

using-directive

简单声明:

decl-specifier-seqopt init-declarator-listopt ;

...

¶5 如果 decl-specifier-seq 包含typedef 说明符,该声明称为typedef 声明和 每个init-declarator的名字 被声明为 typedef-name, 与其关联类型的同义词 (7.1.3)。

§7.1 说明符 [dcl.spec]

可以在声明中使用的说明符是

decl-说明符:

存储类说明符

类型说明符

函数说明符

friend

typedef

decl-specifier-seq:

decl-specifier-seqopt

decl-specifier

§7.1.1 存储类说明符 [dcl.stc]

存储类说明符:

auto

register

static

extern

mutable

§7.1.2 函数说明符 [dcl.fct.spec]

函数说明符:

inline

virtual

explicit

§7.1.3 typedef 说明符 [dcl.typedef]

包含 decl 说明符的声明 typedef 声明以后可以用于命名的标识符 基本 (3.9.1) 或复合 (3.9.2) 类型。 typedef 说明符不得在函数定义中使用 (8.4),并且它不应组合在 decl-specifier-seq 中 与任何其他类型的说明符,除了 类型说明符。

类型定义名称:

标识符

...

在给定范围内,typedef 说明符可用于重新定义在该范围内声明的任何类型的名称 引用它已经引用的类型。 [示例:

typedef struct s { /* ... */ } s;
typedef int I;
typedef int I;
typedef I I;

——结束示例]

§7.1.4 朋友说明符 [dcl.friend]

friend 说明符用于指定对类成员的访问权限;见 11.4。

§7.1.5 类型说明符 [dcl.type]

类型说明符:

简单类型说明符

类说明符

枚举说明符

详细类型说明符

cv-qualifier


由于 §7 ¶5 说 typedef 名称来自 init-declarator 并且 init-declarator-list 被标记为 'opt em>',我认为这意味着在 C++ 中可以省略 typedef 名称,就像在 C 中一样。

【讨论】:

  • 所以它在 C 标准中。让我在 C++ 中找到它。编辑:实际上,在 C++ 中,typedef 是作为额外情况处理的。见§7.1(.1)。
  • @Jonathan,这是否意味着此语法在 C 中有效而在 C++ 中无效?至少从名称decl-specifier 我推断这必须在标识符的声明中,并且由于没有标识符,这将是无效的吗? (一些 C++ 编译器接受它是另一回事。)
  • 这确实为我对 typedef 的一些问题提供了解决方案,因此我接受了它。但它没有解释为什么typedef struct {}; 在 g++ 中被诊断为错误而typedef enum {}; 不是(在空的未命名结构上,gcc 只是警告,clang/clang++ 警告,comeau 在 C 和 C++ 模式下都拒绝代码。 ..)
  • @David 这似乎是 GCC 中的一个错误。如果 decl-specifier-seq 没有​​在翻译单元中引入任何名称,这只是 C++ 中的错误。似乎 GCC 假定任何枚举都有枚举数并将其声明为名称。但是对于一个空枚举,这个假设当然不成立。
【解决方案3】:

在我看来,这确实像是 C 与 C++ 的区别。 C++ 隐式地将结构和联合类型定义到它们的标签中;所以添加 typedef 是多余的,但不是错误。我不知道这是否也适用于枚举。

接下来要做的是查看在这些声明之后允许哪些变量定义。

enum test etest;
test etest2;
struct named snamed;
named snamed2;

【讨论】:

  • 这个答案与问题无关,具体是typedef中的typedef-name是否可选。此外,在 C++ 中使用 typedef 并不是多余的,因为它会影响代码的解释,您可以在此 answer 中阅读更多内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-29
  • 2010-10-22
  • 1970-01-01
相关资源
最近更新 更多