C++11标准规定了类型说明符中允许使用的类型定义吗?

10
在C++11中,类型说明符包括类说明符枚举说明符。(又称类定义和枚举定义)
根据语法规则,类型说明符可以出现在语言的多个位置,但并非所有这些位置都允许使用类说明符和枚举说明符。
例如:
struct C{} c;
// ok: types may be defined in the specifiers of a simple declaration

void f(struct S{});
// error: types may not be defined in parameter types

constexpr auto i = sizeof(enum E{});
// error: types may not be defined in ‘sizeof’ expressions
在标准中,它将类型说明符的使用划分为可以和不可以定义类型的情况,规则在哪里?例如,在哪里规定了在sizeof表达式中不能定义类型的规则?

可以推断出,至少对于sizeof,这可以从5.3/1中指定的“一元表达式”语法中推断出来,但是仅凭粗略的查看并不明显(由于递归定义)。 - Mark B
+1,好问题。但是在这个特定的主题上阅读标准会让人感到迷惑,引用到处都是。顺便问一下,你能否举一个实际需要使用这些东西的例子吗?换句话说,难道不总是可以将定义分成两部分吗? - TemplateRex
1
@MarkB: sizeof(type-id) -> type-id -> type-specifer-seq -> type-specifier -> class-specifier. 附录A是语法摘要。从句法上讲,这是允许的,因此一定有某个地方的文本说明您不能在那里定义类型。 - Andrew Tomazos
1
啊,如果Clang 4.X能在编译错误中显示标准引用就好了;-) - TemplateRex
1
@TemplateRex:这个想法其实不错。 不过匹配的工作量还是蛮大的。 - Andrew Tomazos
CWG 686 可能会很有趣。 - dyp
2个回答

8
无法在C++标准中找到它的原因是因为它在从C标准的增量中实际上被禁止了。在C.1.4中,我们有以下内容:Change: Types must be declared in declarations, not in expressions In C, a sizeof expression or cast expression may create a new type. 这显示了所讨论的禁令。这在7.1.6/3中明确指出:

除非声明构造函数、析构函数或转换函数,否则声明中需要至少一个不是cv-限定符的类型说明符。类型说明符序列不得定义类或枚举,除非它出现在别名声明(7.1.3)的类型标识符中(该别名声明不是模板声明的声明)。

其中特别感兴趣的部分是 类型说明符序列不得定义类或枚举,除非...

1
遗憾的是,该部分是“信息性的”,而不是“规范性的”——它旨在告诉您文档的其余部分内容,不应定义新信息。我试图找到它的“规范性”文本,但未能成功。 - Yakk - Adam Nevraumont
@hvd:不,它是一个声明说明符序列,声明说明符序列从不是类型说明符序列,尽管声明说明符有时是类型说明符。 - Andrew Tomazos
@AndrewTomazos 啊,谢谢,这是一个类型说明符(声明说明符可以是一个类型说明符),但不是一个类型说明符序列。 - user743382
2
有趣的是,这似乎允许在for-range-declaration中进行类型定义:for (struct S2 {} s2: s) { },GCC使用-std=c++11 -pedantic接受它而不需要任何诊断,但clang会给出明确的“错误:类型不能在for range声明中定义”的错误提示。 - user743382
@AndrewTomazos 那么对于一个条件呢? if (struct S2 {} s2 = s1) {} 被两个编译器都拒绝了,即使我将 S2 转换为 bool 也不行。所以如果有一些我找不到的规范文本,我也不会感到惊讶。 - user743382
显示剩余7条评论

3
从N3797开始:
8.3.5/9函数的返回类型或参数类型中不应定义类型。除非该函数被删除(8.4.3)或该定义位于该类的成员说明符内(包括在定义的嵌套类内),否则参数或函数定义的返回类型不得是不完全的类类型(可能带cv修饰符)。
这个规定阻止在函数声明中定义新类型。
下面两个是其他未被OP提及的特殊情况:
11.3/2类不能在友元声明中定义。
14.1/2不能在模板参数声明中定义类型。
最后,此条款阻止在sizeof和其他地方进行定义:
7.1.6/3类型说明序列不得定义类别或枚举类型,除非它出现在不是模板声明的别名声明(7.1.3)中。
请注意,C没有这种限制(C.1.4)。
此外,在C++标准的以前版本中,我们有:
5.3.3p5不应在sizeof表达式中定义类型
我找不到最新版本的标准提案中提到它,而在N3797下是多余的,因为sizeof在语法中定义类型的路线是通过type-specifier-seq,并且被7.1.6/3阻止:
sizeof(type-id) -> type-id -> type-specifer-seq -> type-specifier -> class-specifier

2
sizeof始终是一个表达式,因此“在sizeof表达式中不应定义类型”与“类型必须在声明中而非表达式中声明”是重复的。 - Mark B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接