c++ - 为什么不能在声明中包含多个 decl-specifier-seq?

标签 c++

以下是无效代码:

int i = 0, double j = 2.0;

标准草案说明了原因:

[N4140/7.1.6]

2 As a general rule, at most one type-specifier is allowed in the complete decl-specifier-seq of a declaration or in a type-specifier-seq or trailing-type-specifier-seq. The only exceptions to this rule are the following:

const can be combined with any type specifier except itself.

volatile can be combined with any type specifier except itself.

signed or unsigned can be combined with char, long, short, or int.

short or long can be combined with int.

long can be combined with double.

long can be combined with long.

是的,它可以防止像 int int 这样的傻事,但我看不出上面发布的无效代码有什么问题。引用 [N4140/7]simple-declarationdecl-specifier-seqopt init-declarator-list<子>选择;

[N4140/8] 然后显示 init-declarator-listinit-declarator-list初始化声明器,

并且一个 init-declarator 是一个 declarator 初始化器opt

由于我们只关心 int i = 0 形式的语法,那么我们关心的 declarator 就是 ptr-declarator,这是一个noptr-declarator,这是一个declarator-id attribute-specifier-seqopt,最后是一个declarator- id 仅由 ...opt id-expression 组成。

为了完整起见,[N4140/5.1.1] 表示 id-expression 可以是 unqualified-id,或者只是 < em>标识符。

如果我到目前为止还没有绊倒,这就是语法所反射(reflect)的。

int decl-specifier-seq

i unqualified-id

= 0 初始化器

int i = 0 init-declarator

由于simple-declarationdecl-specifier-seq,只有一个decl-specifier-seq适用于整个init-declarator-list

有趣的是,这意味着你不能做这样的事情:

int i, const j;

还是:

int i, * j;

是完全合法的,因为星号是 ptr-operator 的一部分。但你不能这样做:

int i, const * j; // pointer to const int

这意味着在下面的代码中,i 变成了一个指向 const int 的指针。惊喜!

int h = 25;
int const * j, * i = &h;
*i = 50; // error: assignment of read-only location '* i'

[N4140/8] 中的意图很明确:

3 Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself.99

99) A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator. That is

T D1, D2, ... Dn;

is usually equivalent to

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

问题是为什么会这样?


如果合法,可以在 for 循环中进行,这有点用处。

最佳答案

简答:一个语句只能允许“一个类型的声明”,但该声明可以声明“多个标识符”。 const/volatile 也是类型限定符或指针限定符,因此它们需要绑定(bind)类型或指针。

长答案:

我从未阅读过标准,但我来了...

“它可以防止像 int int 这样愚蠢的事情,但我看不出上面发布的无效代码有什么问题。”

  • 我的看法是一个语句只能允许一个声明,但一个声明可以允许你声明多个相同类型的标识符。
  • 所以问题 int i = 0, double j = 2.0;是你有两种类型,int和double。这违反了 [N4140/7.1.6]。
  • 这是语言允许的,因此上述说法不正确。

但是你继续深入挖掘,我相信你的困惑从这里开始“因为我们只关心形式的语法......”。声明不会分解如下吗?

int i = 0 ::= simple-declaration
Where in...
int ::= type-specifier
i ::= identifier
= 0 ::= init-declarator

更多

你提到...

Not Allowed: int i, const j;
Allowed: int i, * j;
Not Allowed: int i, const * j; // pointer to const int
Allowed: int const * j, * i = &h;

我的回答:

Not Allowed: int i, const j; - because const is a type modifier, syntactically there is no type specified to bind to.
Allowed: int i, * j; - because * grabs the type int and j has a complete type.
Not Allowed: int i, const * j; - because const is not associated to a type here. It is the same problem as in the first case. Read it as j is a pointer to <unexpected word in between> and thus screws up the grammar.
Allowed: int const * j, * i = &h; - because syntactically const has a type to bind to.

“问题是为什么会这样?”

  • 当我学习 C 时,我最初对在类型名称之前/之后使用 const 感到困惑,为了消除混淆,我尝试了一些测试代码并弄清楚该语言允许什么,以下是我想出的和。它来 self 的旧笔记。它绝对看起来像是一个新程序员制作的东西。但是,它消除了大部分疑虑。

    [存储类] [符号限定符] [大小限定符] [类型限定符] <[* [类型限定符]] [符号名称] [[] | ([参数])]>

    存储类:auto、register、static、extern、typedef

    符号限定符:有符号、无符号

    大小限定符:short、long、long long

    基本类型:char、int、float、double、void

    类型限定符:const、volatile

    符号名称可以是变量、常量、类型(定义)名称和函数

    符号前面的 * 使它成为一个指针。 * 可以出现N次,使其成为指针对指针等等。

    符号后缀的 [] 使其成为一个数组。 [] 可以出现 N 次,是一个多维数组。

    符号后缀的 () 使其成为函数。 () 可以出现 N 次,但是由于函数不能返回函数,所以 () 可以在函数返回函数指针时再次出现。

上面的内容帮助我在声明变量时思考得更清楚。

从我古老的笔记中修改类型说明符语法:

[storage class] [sign qualifier] [size qualifier] <type> [type qualifiers] [* [pointer qualifiers]] [symbol name] [[] | ([parameters])]

也就是说 const 和 volatile 要么是类型限定符,要么是指针限定符,它们需要绑定(bind)类型或指针来限定它们。

考虑“一个语句只能允许您进行一个声明,但一个声明可以允许您声明多个相同类型的标识符”的想法。这意味着类型说明符语法可以分解如下:

type ::= [storage class] [sign qualifier] [size qualifier] <type> [type qualifiers]

symbol ::= [* [pointer qualifiers]] [symbol name] [[] | ([parameters])]

声明的简化语法是:

输入符号[,符号...]

很明显,

int i, 常量 j; - 不符合语法。

int 常量 i, j; - 符合语法。

我确信熟悉标准的人可以使用标准并使用正确的术语和引用资料提供答案。但是,请记住,经验不足的程序员可能会发现技术含量较低且易于理解的答案。

如果允许“int i, const j”的形式,那么可以写成“int const i, const j”,这意味着 j 是一个双常量。这没有任何意义。

关于c++ - 为什么不能在声明中包含多个 decl-specifier-seq?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27432506/

相关文章:

C++ 初学者 : what is the point of using a variable by reference if using "const"?

c++ - 用标准 C 或 C++ 编写的解释器

c++ - 在 unordered_map 中保存 shared_ptr - 立即调用析构函数

c++ - 如何选择构造函数模板?

C++ - CreateRemoteThread DLL 注入(inject) [Windows 7]

c++ - boost::线程编译错误

c++ - 有条件地启用非模板函数c++

C++ 右值引用和 move 语义

c# - 当参数之一是指针数组时如何从 C# 调用 C++ DLL

c++ - 无法从文本文件中读取并存储到字符串中