Declarations in C++

前端 未结 2 407
星月不相逢
星月不相逢 2021-01-31 07:34

From what I have understood, declarations/initializations in C++ are statements with \'base type\' followed by a comma separated list of declarators.

Consider the follow

相关标签:
2条回答
  • 2021-01-31 08:14

    Good question, with a complicated answer. To really grasp this, you need to understand the internal structure of C++ declarations quite thoroughly.

    (Note that in this answer, I will totally omit the existence of attributes to prevent overcomplication).

    A declaration has two components: a sequence of specifiers, followed by a comma-separated list of init-declarators.

    Specifiers are things like:

    • storage class specifiers (e.g. static, extern)
    • function specifiers (e.g. virtual, inline)
    • friend, typedef, constexpr
    • type specifiers, which include:
      • simple type specifiers (e.g. int, short)
      • cv-qualifiers (const, volatile)
      • other things (e.g. decltype)

    The second part of a declaration are the comma-separated init-declarators. Each init-declarator consists of a sequence of declarators, optionally followed by an initialiser.

    What declarators are:

    • identifier (e.g. the i in int i;)
    • pointer-like operators (*, &, &&, pointer-to-member syntax)
    • function parameter syntax (e.g. (int, char))
    • array syntax (e.g. [2][3])
    • cv-qualifiers, if these follow a pointer declarator.

    Notice that the declaration's structure is strict: first specifiers, then init-declarators (each being declarators optionally followed by an initialiser).

    The rule is: specifiers apply to the entire declaration, while declarators apply only to the one init-declarator (to the one element of the comma-separated list).

    Also notice above that a cv-qualifier can be used as both a specifier and a declarator. As a declarator, the grammar restricts them to only be used in the presence of pointers.

    So, to handle the four declarations you have posted:

    1

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

    The specifier part contains just one specifier: int. That is the part that all declarators will apply to.

    There are two init-declarators: i = 0 and * const p = &i.

    The first one has one declarator, i, and an initialiser = 0. Since there is no type-modifying declarator, the type of i is given by the specifiers, int in this case.

    The second init-declarator has three declarators: *, const, and p. And an initialiser, = &i.

    The declarators * and const modify the base type to mean "constant pointer to the base type." The base type, given by specifiers, is int, to the type of p will be "constant pointer to int."

    2

    int j = 0, const c = 2;
    

    Again, one specifier: int, and two init-declarators: j = 0 and const c = 2.

    For the second init-declarator, the declarators are const and c. As I mentioned, the grammar only allows cv-qualifiers as declarators if there is a pointer involved. That is not the case here, hence the error.

    3

    int *const p1 = nullptr, i1 = 0;
    

    One specifier: int, two init-declarators: * const p1 = nullptr and i1 = 0.

    For the first init-declarator, the declarators are: *, const, and p1. We already dealt with such an init-declarator (the second one in case 1). It adds the "constant pointer to base type" to the specifier-defined base type (which is still int).

    For the second init-declarator i1 = 0, it's obvious. No type modifications, use the specifier(s) as-is. So i1 becomes an int.

    4

    int const j1 = 0, c1 = 2;
    

    Here, we have a fundamentally different situation from the preceding three. We have two specifiers: int and const. And then two init-declarators, j1 = 0 and c1 = 2.

    None of these init-declarators have any type-modifying declarators in them, so they both use the type from the specifiers, which is const int.

    0 讨论(0)
  • 2021-01-31 08:16

    This is specified in [dcl.dcl] and [dcl.decl] as part of the simple-declaration* and boils down to differences between the branches in ptr-declarator:

    declaration-seq:
        declaration
    
    declaration:
        block-declaration
    
    block-declaration:
        simple-declaration
    
    simple-declaration:
        decl-specifier-seqopt init-declarator-listopt ;
    ----
    
    decl-specifier-seq:
        decl-specifier decl-specifier-seq    
    
    decl-specifier:    
        type-specifier                               ← mentioned in your error
    
    type-specifier:
        trailing-type-specifier
    
    trailing-type-specifier:
        simple-type-specifier
        cv-qualifier
    ----
    
    init-declarator-list:
       init-declarator
       init-declarator-list , init-declarator
    
    init-declarator:
       declarator initializeropt
    
    declarator:
        ptr-declarator
    
    ptr-declarator:                                 ← here is the "switch"
        noptr-declarator
        ptr-operator ptr-declarator
    
    ptr-operator:                                   ← allows const
        *  cv-qualifier-seq opt
    
    cv-qualifier:
        const
        volatile
    
    noptr-declarator:                               ← does not allow const
        declarator-id
    
    declarator-id:
        id-expression
    

    The important fork in the rules is in ptr-declarator:

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

    Essentially, noptr-declarator in your context is an id-expression only. It may not contain any cv-qualifier, but qualified or unqualified ids. However, a ptr-operator may contain a cv-qualifier.

    This indicates that your first statement is perfectly valid, since your second init-declarator

     *const p = &i;
    

    is a ptr-declarator of form ptr-operator ptr-declarator with ptr-operator being * const in this case and ptr-declarator being a unqualified identifier.

    Your second statement isn't legal because it is not a valid ptr-operator:

     const c = 2
    

    A ptr-operator must start with *, &, && or a nested name specifier followed by *. Since const c does not start with either of those tokens, we consider const c as noptr-declarator, which does not allow const here.

    Also, why the behaviour differs among 3rd and 4th statements?

    Because int is the type-specifier, and the * is part of the init-declarator,

    *const p1
    

    declares a constant pointer.

    However, in int const, we have a decl-specifier-seq of two decl-specifier, int (a simple-type-specifier) and const (a cv-qualifier), see trailing-type-specifier. Therefore both form one declaration specifier.


    * Note: I've omitted all alternatives which cannot be applied here and simplified some rules. Refer to section 7 "Declarations" and section 8 "Declarators" of C++11 (n3337) for more information.

    0 讨论(0)
提交回复
热议问题