本文介绍了什么是声明和声明符,标准如何解释它们的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

标准是如何定义的,例如,float (*(*(&e)[10])())[5] 声明了一个类型为对数组的引用"的变量10 个指向函数的指针 () 返回指向 5 float"?

How exactly does the standard define that, for example, float (*(*(&e)[10])())[5] declares a variable of type "reference to array of 10 pointer to function of () returning pointer to array of 5 float"?

推荐答案

声明在C++的语法中被称为简单声明,它们是以下两种形式之一(§7/1):

Declarations of the type we're concerned with are known as simple-declarations in the grammar of C++, which are of one of the following two forms (§7/1):

decl-specifier-seq init-declarator-list ;
attribute-specifier-seq decl-specifier-seq init-declarator-list ;

attribute-specifier-seq 是一系列属性 ([[something]]) 和/或对齐说明符 (alignas(something)).由于这些不影响声明的类型,我们可以忽略它们和上述两种形式中的第二种.

The attribute-specifier-seq is a sequence of attributes ([[something]]) and/or alignment specifiers (alignas(something)). Since these don't affect the type of the declaration, we can ignore them and the second of the above two forms.

所以我们声明的第一部分,decl-specifier-seq,由声明说明符组成.其中包括一些我们可以忽略的东西,例如存储说明符(staticextern 等)、函数说明符(inline 等).)、friend 说明符等.然而,我们感兴趣的一个声明说明符是类型说明符,它可能包含简单的类型关键字(charintunsigned 等)、用户定义类型的名称、cv 限定符(constvolatile),以及其他我们不关心的.

So the first part of our declaration, the decl-specifier-seq, is made up of declaration specifiers. These include some things that we can ignore, such as storage specifiers (static, extern, etc.), function specifiers (inline, etc.), the friend specifier, and so on. However, the one declaration specifier of interest to us is the type specifier, which may include simple type keywords (char, int, unsigned, etc.), names of user-defined types, cv-qualifiers (const or volatile), and others that we don't care about.

示例:decl-specifier-seq 的一个简单示例是const int,它只是类型说明符的序列.另一个可能是 unsigned int volatile.

Example: So a simple example of a decl-specifier-seq which is just a sequence of type specifiers is const int. Another one could be unsigned int volatile.

您可能会想哦,那么像 const volatile int int float const 这样的东西也是 decl-specifier-seq 吗?"它符合语法规则是对的,但语义规则不允许这样的 decl-specifier-seq.事实上,只允许一种类型说明符,除了某些组合(例如 unsignedintconst 与除自身之外的任何东西)以及在至少需要一个非 cv 限定符(第 7.1.6/2-3 节).

You may think "Oh, so something like const volatile int int float const is also a decl-specifier-seq?" You'd be right that it fits the rules of the grammar, but the semantic rules disallow such a decl-specifier-seq. Only one type specifier is allowed, in fact, except for certain combinations (such as unsigned with int or const with anything except itself) and at least one non-cv-qualifier is required (§7.1.6/2-3).

快速测验(您可能需要参考标准)

Quick Quiz (you might need to reference the standard)

  1. const int const 是否是有效的声明说明符序列?如果不是,是不是语法或语义规则不允许?

  1. Is const int const a valid declaration specifier sequence or not? If not, is it disallowed by the syntactic or semantic rules?

  • unsigned const int 是否是有效的声明说明符序列?如果不是,是不是语法或语义规则不允许?

  • Is unsigned const int a valid declaration specifier sequence or not? If not, is it disallowed by the syntactic or semantic rules?

  • auto const 是否是有效的声明说明符序列?如果不是,是不是语法或语义规则不允许?

  • Is auto const a valid declaration specifier sequence or not? If not, is it disallowed by the syntactic or semantic rules?

  • int * const 是否是有效的声明说明符序列?如果不是,是不是语法或语义规则不允许?

  • Is int * const a valid declaration specifier sequence or not? If not, is it disallowed by the syntactic or semantic rules?

  • 声明符

    简单声明的第二部分是init-declarator-list.它是由逗号分隔的一系列声明符,每个声明符都有一个可选的初始值设定项(第 8 节).每个声明符在程序中引入一个变量或函数.声明符的最简单形式就是您要引入的名称 - declarator-id.声明 int x, y = 5; 有一个声明说明符序列,它只是 int,后跟两个声明符,xy,其中第二个有一个初始化程序.然而,我们将在本文的其余部分忽略初始化器.

    Declarators

    The second part of a simple-declaration is the init-declarator-list. It is a sequence of declarators separated by commas, each with an optional initializer (§8). Each declarator introduces a single variable or function into the program. The most simple form of declarator is just the name you're introducing - the declarator-id. The declaration int x, y = 5; has a declaration specifier sequence that is just int, followed by two declarators, x and y, the second of which has an initializer. We will, however, ignore initializers for the rest of this post.

    声明符可以具有特别复杂的语法,因为这是声明的一部分,允许您指定变量是否是指针、引用、数组、函数指针等.请注意,这些都是 declarator 而不是整个声明.这正是 int* x, y; 不声明两个指针的原因——星号 *x 声明符的一部分,不是 y 声明符的一部分.一个重要的规则是每个声明符必须只有一个 declarator-id - 它声明的名称.一旦确定了声明的类型(我们稍后会谈到),就会强制执行有关有效声明符的其余规则.

    A declarator can have a particularly complex syntax because this is the part of the declaration that allows you to specify whether the variable is a pointer, reference, array, function pointer, etc. Note that these are all part of the declarator and not the declaration as a whole. This is precisely the reason why int* x, y; does not declare two pointers - the asterisk * is part of the declarator of x and not part of the declarator of y. One important rule is that every declarator must have exactly one declarator-id - the name it is declaring. The rest of the rules about valid declarators are enforced once the type of the declaration is determined (we'll come to it later).

    示例:声明符的一个简单示例是*const p,它声明了一个const 指针,指向...某物.它指向的类型由其声明中的声明说明符给出.一个更可怕的例子是问题中给出的例子,(*(*(&e)[10])())[5],它声明了一个对函数指针数组的引用再次返回指向...的指针,类型的最后部分实际上是由声明说明符给出的.

    Example: A simple example of a declarator is *const p, which declares a const pointer to... something. The type it points to is given by the declaration specifiers in its declaration. A more terrifying example is the one given in the question, (*(*(&e)[10])())[5], which declares a reference to an array of function pointers that return pointers to... again, the final part of the type is actually given by the declaration specifiers.

    您不太可能遇到如此可怕的声明符,但有时确实会出现类似的声明符.能够阅读问题中的声明是一项有用的技能,并且是一项伴随练习而来的技能.了解标准如何解释声明的类型会很有帮助.

    You're unlikely to ever come across such horrible declarators but sometimes similar ones do appear. It's a useful skill to be able to read a declaration like the one in the question and is a skill that comes with practice. It is helpful to understand how the standard interprets the type of a declaration.

    快速测验(您可能需要参考标准)

    Quick Quiz (you might need to reference the standard)

    1. int const unsigned* const array[50]; 的哪些部分是声明说明符和声明符?

    1. Which parts of int const unsigned* const array[50]; are the declaration specifiers and the declarator?

  • volatile char (*fp)(float const), &r = c; 的哪些部分是声明说明符和声明符?

  • Which parts of volatile char (*fp)(float const), &r = c; are the declaration specifiers and the declarators?

  • 声明类型

    现在我们知道声明是由声明符说明符序列和声明符列表组成的,我们可以开始考虑如何确定声明的类型.例如,很明显 int* p;p 定义为指向 int 的指针",但对于其他类型,它并不那么明显.

    Declaration Types

    Now we know that a declaration is made up of a declarator specifier sequence and a list of declarators, we can begin to think about how the type of a declaration is determined. For example, it might be obvious that int* p; defines p to be a "pointer to int", but for other types it's not so obvious.

    具有多个声明符的声明,假设有 2 个声明符,被认为是特定标识符的两个声明.即int x, *y;是标识符xint x的声明,以及标识符y, int *y.

    A declaration with multiple declarators, let's say 2 declarators, is considered to be two declarations of particular identifiers. That is, int x, *y; is a declaration of identifier x, int x, and a declaration of identifier y, int *y.

    类型在标准中表示为类似英语的句子(例如指向 int 的指针").这种类似英语形式的声明类型的解释分为两部分.首先,确定声明说明符的类型.其次,对整个声明应用递归程序.

    Types are expressed in the standard as English-like sentences (such as "pointer to int"). The interpretation of a declaration's type in this English-like form is done in two parts. First, the type of the declaration specifier is determined. Second, a recursive procedure is applied to the declaration as a whole.

    声明说明符序列的类型由标准的表 10 确定.它列出了序列的类型,因为它们以任何顺序包含相应的说明符.因此,例如,任何以任何顺序包含 signedchar 的序列,包括 char signed,都具有signed char"类型.任何出现在声明说明符序列中的 cv 限定符都被添加到类型的前面.所以 char const signed 的类型是const signed char".这可以确保无论您放置说明符的顺序如何,类型都是相同的.

    The type of a declaration specifier sequence is determined by Table 10 of the standard. It lists the types of the sequences given that they contain the corresponding specifiers in any order. So for example, any sequence that contains signed and char in any order, including char signed, has type "signed char". Any cv-qualifier that appears in the declaration specifier sequence is added to the front of the type. So char const signed has type "const signed char". This makes sure that regardless of what order you put the specifiers, the type will be the same.

    快速测验(您可能需要参考标准)

    Quick Quiz (you might need to reference the standard)

    1. 声明说明符序列int long const unsigned的类型是什么?

  • 声明说明符序列char volatile的类型是什么?

  • 声明说明符序列auto const的类型是什么?

  • 声明类型

    现在我们有了声明说明符序列的类型,我们可以计算出一个标识符的整个声明的类型.这是通过应用 §8.3 中定义的递归过程来完成的.为了解释这个过程,我将使用一个运行示例.我们将在 float const (*(*(&e)[10])())[5].

    步骤 1 第一步是将声明拆分为 TD 形式,其中 T 是声明说明符序列,D 是声明符.所以我们得到:

    Step 1 The first step is to split the declaration into the form T D where T is the declaration specifier sequence and D is the declarator. So we get:

    T = float const
    D = (*(*(&e)[10])())[5]
    

    T 的类型当然是const float",正如我们在上一节中确定的那样.然后我们寻找与 D 的当前形式匹配的 §8.3 小节.您会发现这是 §8.3.4 数组,因为它声明它适用于 T D 形式的声明,其中 D 具有以下形式:

    The type of T is, of course, "const float", as we determined in the previous section. We then look for the subsection of §8.3 that matches the current form of D. You'll find that this is §8.3.4 Arrays, because it states that it applies to declarations of the form T D where D has the form:

    D1 [ constant-expression ] attribute-specifier-seq

    我们的 D 确实是那种形式,其中 D1(*(*(&e)[10])()).

    Our D is indeed of that form where D1 is (*(*(&e)[10])()).

    现在想象一个声明T D1(我们已经去掉了[5]).

    Now imagine a declaration T D1 (we've gotten rid of the [5]).

    T D1 = const float (*(*(&e)[10])())
    

    它的类型是 T".本节说明我们的标识符 e 的类型是<some stuff> 5 T 数组",其中 <some stuff>与虚声明的类型相同.因此,要计算出类型的其余部分,我们需要计算出T D1 的类型.

    It's type is "<some stuff> T". This section states that the type of our identifier, e, is "<some stuff> array of 5 T", where <some stuff> is the same as in the type of the imaginary declaration. So to work out the remainder of the type, we need to work out the type of T D1.

    这就是递归!我们递归地计算出声明的内部部分的类型,在每一步都去掉一点.

    This is the recursion! We recursively work out the type of an inner part of the declaration, stripping a bit of it off at every step.

    第 2 步 因此,和以前一样,我们将新声明拆分为 T D 形式:

    Step 2 So, as before, we split our new declaration into the form T D:

    T = const float
    D = (*(*(&e)[10])())
    

    这与段落 §8.3/6 匹配,其中 D 的形式为 ( D1 ).这个例子很简单,T D的类型就是T D1的类型:

    This matches paragraph §8.3/6 where D is of the form ( D1 ). This case is simple, the type of T D is simply the type of T D1:

    T D1 = const float *(*(&e)[10])()
    

    第 3 步现在我们称之为 T D 并再次拆分它:

    Step 3 Let's call this T D now and split it up again:

    T = const float
    D = *(*(&e)[10])()
    

    这匹配 §8.3.1 指针,其中 D 的形式为 * D1.如果 T D1 的类型为 T",则 TD 的类型为 指向 的指针T".所以现在我们需要T D1的类型:

    This matches §8.3.1 Pointers where D is of the form * D1. If T D1 has type "<some stuff> T", then T D has type "<some stuff> pointer to T". So now we need the type of T D1:

    T D1 = const float (*(&e)[10])()
    

    第 4 步我们称之为 T D 并将其拆分:

    Step 4 We call it T D and split it up:

    T = const float
    D = (*(&e)[10])()
    

    这匹配 §8.3.5 函数,其中 D 的形式为 D1 ().如果 T D1 的类型为 T",那么 TD 的类型为"() 返回的函数T".所以现在我们需要T D1的类型:

    This matches §8.3.5 Functions where D is of the form D1 (). If T D1 has type "<some stuff> T", then T D has type "<some stuff> function of () returning T". So now we need the type of T D1:

    T D1 = const float (*(&e)[10])
    

    第 5 步我们可以应用与第 2 步相同的规则,其中声明符简单地以括号结尾:

    Step 5 We can apply the same rule we did for step 2, where the declarator is simply parenthesised to end up with:

    T D1 = const float *(&e)[10]
    

    第 6 步当然,我们把它分开:

    T = const float
    D = *(&e)[10]
    

    我们再次将第 8.3.1 节指针与 * D1 形式的 D 匹配.如果 T D1 的类型为 T",则 TD 的类型为 指向 的指针T".所以现在我们需要T D1的类型:

    We match §8.3.1 Pointers again with D of the form * D1. If T D1 has type "<some stuff> T", then T D has type "<some stuff> pointer to T". So now we need the type of T D1:

    T D1 = const float (&e)[10]
    

    步骤 7 拆分:

    T = const float
    D = (&e)[10]
    

    我们再次匹配第 8.3.4 节数组,D 的形式为 D1 [10].如果 T D1 的类型为 T",则 TD 的类型为 10 数组>T".那么T D1的类型是什么?

    We match §8.3.4 Arrays again, with D of the form D1 [10]. If T D1 has type "<some stuff> T", then T D has type "<some stuff> array of 10 T". So what is T D1's type?

    T D1 = const float (&e)
    

    步骤 8 再次应用括号步骤:

    T D1 = const float &e
    

    步骤 9 拆分:

    T = const float
    D = &e
    

    现在我们匹配 §8.3.2 引用,其中 D 的形式为 &D1.如果 T D1 的类型为<some stuff> T",则 TD 的类型为<some stuff>对 的引用T".那么T D1的类型是什么?

    Now we match §8.3.2 References where D is of the form & D1. If T D1 has type "<some stuff> T", then T D has type "<some stuff> reference to T". So what is the type of T D1?

    T D1 = const float e
    

    第 10 步当然只是T"!没有<一些东西>在这个级别.这是由 §8.3/5 中的基本情况规则给出的.

    Step 10 Well it's just "T" of course! There is no <some stuff> at this level. This is given by the base case rule in §8.3/5.

    我们完成了!

    所以现在如果我们看一下我们在每一步确定的类型,代入下面每一层的<some stuff>s,我们可以确定float const中e的类型(*(*(&e)[10])())[5]:

    So now if we look at the type we determined at each step, substituting the <some stuff>s from each level below, we can determine the type of e in float const (*(*(&e)[10])())[5]:

    <some stuff> array of 5 T
    │          └──────────┐
    <some stuff> pointer to T
    │          └────────────────────────┐
    <some stuff> function of () returning T
    |          └──────────┐
    <some stuff> pointer to T
    |          └───────────┐
    <some stuff> array of 10 T
    |          └────────────┐
    <some stuff> reference to T
    |          |
    <some stuff> T
    

    如果我们把这一切结合起来,我们得到的是:

    If we combine this all together, what we get is:

    reference to array of 10 pointer to function of () returning pointer to array of 5 const float
    

    不错!这显示了编译器如何推断声明的类型.请记住,如果有多个声明符,这将应用于标识符的每个声明.尝试弄清楚这些:

    Nice! So that shows how the compiler deduces the type of a declaration. Remember that this is applied to each declaration of an identifier if there are multiple declarators. Try figuring out these:

    快速测验(您可能需要参考标准)

    Quick Quiz (you might need to reference the standard)

    1. 声明bool **(*x)[123];x的类型是什么?

  • 声明中yz的类型是什么int const signed *(*y)(int), &z =我;?

  • 这篇关于什么是声明和声明符,标准如何解释它们的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

    05-27 18:19
    查看更多