【问题标题】:Declarations in C++C++ 中的声明
【发布时间】:2016-10-09 21:45:15
【问题描述】:

据我了解,C++ 中的声明/初始化是带有“基本类型”的语句,后跟以逗号分隔的声明符列表。

考虑以下声明:

int i = 0, *const p = &i; // Legal, the so-called base type is 'int'.
                          // i is an int while p is a const pointer to an int.

int j = 0, const c = 2;   // Error: C++ requires a type specifier for all declarations.
                          // Intention was to declare j as an int and c an as const int.

int *const p1 = nullptr, i1 = 0; // p1 is a const pointer to an int while i1 is just an int.

int const j1 = 0, c1 = 2;   // Both j1 and c1 are const int.

const int 是基本类型还是复合类型?

从上面第二个声明的错误来看,它似乎是一个基类型。如果是这样,那么第一个声明呢?

换句话说,如果第一个语句是合法的,为什么第二个不是?另外,为什么第三个和第四个语句的行为不同?

【问题讨论】:

  • 将多个声明放在一行中可能会导致混淆。即使你做对了,下一个读者也可能不理解(下一个读者可能是几个月后的你)。恕我直言,最佳做法是每行声明一个变量。但不要误会我的意思,了解它的工作原理当然很好
  • C++ 中没有基本类型这样的东西。 int 是一种类型,而 const*& 是类型限定符的示例。声明使用类型和(可选)类型限定符来创建标识符。
  • @Peter C++ 中也没有“类型限定符”。 const 是一个 cv 限定符(它既可以是声明符也可以是说明符),而 *& 是声明符。
  • @tobi303:我认为在一行中声明的多个变量没有任何问题......只要它们具有相同的类型。当你开始混合类型时,我会擦亮我的电锯。
  • 这对初学者来说真是个问题!欢迎使用 C++,我期待您在未来做出贡献。

标签: c++ pointers constants language-lawyer declaration


【解决方案1】:

好问题,答案很复杂。要真正掌握这一点,您需要彻底了解 C++ 声明的内部结构。

(请注意,在此答案中,我将完全省略属性的存在以防止过于复杂)。

一个声明有两个部分:一个说明符序列,后面是一个逗号分隔的init-declarators列表

说明符是这样的:

  • 存储类说明符(例如staticextern
  • 函数说明符(例如virtualinline
  • friend, typedef, constexpr
  • 类型说明符,包括:
    • 简单类型说明符(例如intshort
    • cv-qualifiers (const, volatile)
    • 其他事情(例如decltype

声明的第二部分是逗号分隔的初始化声明符。每个 init-declarator 由一系列 declarators 组成, 可选地后跟一个 initialiser。

什么是声明符:

  • 标识符(例如int i;中的i
  • 类指针运算符(*&&&、指向成员的语法)
  • 函数参数语法(例如(int, char)
  • 数组语法(例如[2][3]
  • cv-qualifiers,如果它们跟在指针声明符之后。

请注意,声明的结构是严格的:首先是说明符,然后是 init-declarators(每个声明符可选地后跟一个初始化器)。

规则是:说明符适用于整个声明,而声明符仅适用于一个 init-declarator(适用于逗号分隔列表的一个元素)。

还请注意,cv 限定符既可以用作说明符,也可以用作声明符。作为声明符,语法将它们限制为仅在存在指针的情况下使用。

所以,要处理您发布的四个声明:

1

int i = 0, *const p = &i;

说明符部分只包含一个说明符:int。这是所有声明符都适用的部分。

有两个初始化声明符:i = 0* const p = &i

第一个有一个声明器 i 和一个初始化器 = 0。由于没有类型修改声明符,i 的类型由说明符给出,在本例中为 int

第二个初始化声明器有三个声明器:*constp。还有一个初始化程序,= &i

声明符*const 将基类型修改为“指向基类型的常量指针”。由说明符给出的基本类型是intp 的类型将是“指向int 的常量指针”。

2

int j = 0, const c = 2;

同样,一个说明符:int,和两个初始化声明符:j = 0const c = 2

对于第二个初始化声明符,声明符是constc。正如我所提到的,如果涉及指针,语法只允许 cv 限定符作为声明符。这里不是这种情况,因此会出现错误。

3

int *const p1 = nullptr, i1 = 0;

一个说明符:int,两个初始化声明符:* const p1 = nullptri1 = 0

对于第一个 init-declarator,声明符是:*constp1。我们已经处理过这样的 init-declarator(第二个在 1 的情况下)。它将“指向基类型的常量指针”添加到说明符定义的基类型(仍然是int)。

对于第二个 init-declarator i1 = 0,很明显。没有类型修改,按原样使用说明符。所以i1 变成了int

4

int const j1 = 0, c1 = 2;

在这里,我们遇到了与前三种完全不同的情况。我们有两个说明符:intconst。然后是两个初始化声明器,j1 = 0c1 = 2

这些初始化声明符中没有任何类型修改声明符,因此它们都使用说明符中的类型,即const int

【讨论】:

  • 所以基本上是显式异常。
  • @Angew 允许 cv 限定符仅使用指针而不是其他方式的逻辑是什么?你能想出一个这样的限制是合理的例子吗?
【解决方案2】:

这在 [dcl.dcl] 和 [dcl.decl] 中指定为 simple-declaration* 的一部分,归结为 ptr-declarator 中分支之间的差异:

声明序列: 宣言 宣言: 块声明 块声明: 简单声明 简单声明: decl-specifier-seqopt init-declarator-listopt ; ---- 声明说明符序列: decl 说明符 decl 说明符序列 声明说明符: 类型说明符 ←在你的错误中提到 类型说明符: 尾随类型说明符 尾随类型说明符: 简单类型说明符 cv 限定符 ---- 初始化声明器列表: 初始化声明器 初始化声明器列表,初始化声明器 初始化声明器: 声明初始化器opt 声明者: ptr 声明符 ptr-declarator: ←这里是“开关” noptr 声明符 ptr 运算符 ptr 声明符 ptr 运算符:←允许 const * cv-qualifier-seq opt cv 限定符: 常量 易挥发的 noptr 声明符:←不允许 const 声明者 ID 声明者 ID: 身份表达

规则中重要的分叉在ptr-declarator

ptr-declarator:
    noptr-declarator
    ptr-operator ptr-declarator

基本上,在您的上下文中,noptr-declarator 仅是 id-expression。它可能不包含任何cv-qualifier,而是合格或不合格的ID。但是,ptr-operator 可能包含cv-qualifier

这表明您的第一个语句完全有效,因为您的第二个 init-declarator

 *const p = &i;

ptr-operator ptr-declarator 形式的ptr-declarator,在这种情况下ptr-operator* constptr-declarator 是一个非限定标识符。

您的第二个声明不合法,因为它不是有效的ptr-operator

 const c = 2

ptr-operator 必须以 *&&& 或后跟 * 的嵌套名称说明符开头。由于const c 不以其中任何一个标记开头,因此我们将const c 视为noptr-declarator,此处不允许const

另外,为什么第三条和第四条语句的行为不同?

因为inttype-specifier,而*init-declarator 的一部分,

*const p1

声明一个常量指针。

但是,在int const 中,我们有两个decl-specifierint(一个simple-type-specifier)和const(一个cv-qualifier)中的一个decl-specifier-seq,请参阅trailing-type-specifier。因此两者都形成一个声明说明符。


* 注意:我省略了所有不能在此处应用的替代方案并简化了一些规则。有关详细信息,请参阅 C++11 (n3337) 的第 7 节“声明”和第 8 节“声明符”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-01
    相关资源
    最近更新 更多