admin管理员组

文章数量:1402806

Where the C++ standard allows an identifier with name space of a structure (a "non-ordinary" identifier in C) in a primary expression?

Consider the following code (which is a valid C++, but an invalid C):

struct s
{
    int x;
    int y[sizeof x];
};

Here the sizeof is applied to structure member x while declaring the structure itself. This is allowed in C++ (and not in C) because:

  1. In C++ the x has class (and a struct is a class) scope. Therefore, as I understand, sizeof will search x in the class scope first.
  2. C++ seems to allow an identifier with name space of a structure (a "non-ordinary" identifier in C) in a primary expression (which is an operand of the sizeof).

To compare with C: C23 (n3299.pdf, 6.5.2p2) has the following constraint (emphasis added):

The identifier in an identifier primary expression shall have a visible declaration as an ordinary identifier that declares an object or a function.

This constraint invalidates the code above (being compiled as C code), because x is (at least) a non-ordinary identifier.


How does C++ treat / classify identifier x (e.g. as an identifier with name space struct s)?

Where does the C++ standard allow an identifier with name space of a structure in a primary expression?

Where the C++ standard allows an identifier with name space of a structure (a "non-ordinary" identifier in C) in a primary expression?

Consider the following code (which is a valid C++, but an invalid C):

struct s
{
    int x;
    int y[sizeof x];
};

Here the sizeof is applied to structure member x while declaring the structure itself. This is allowed in C++ (and not in C) because:

  1. In C++ the x has class (and a struct is a class) scope. Therefore, as I understand, sizeof will search x in the class scope first.
  2. C++ seems to allow an identifier with name space of a structure (a "non-ordinary" identifier in C) in a primary expression (which is an operand of the sizeof).

To compare with C: C23 (n3299.pdf, 6.5.2p2) has the following constraint (emphasis added):

The identifier in an identifier primary expression shall have a visible declaration as an ordinary identifier that declares an object or a function.

This constraint invalidates the code above (being compiled as C code), because x is (at least) a non-ordinary identifier.


How does C++ treat / classify identifier x (e.g. as an identifier with name space struct s)?

Where does the C++ standard allow an identifier with name space of a structure in a primary expression?

Share Improve this question edited Mar 25 at 15:03 pmor asked Mar 21 at 15:16 pmorpmor 6,4984 gold badges24 silver badges49 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 3

In C opposite to C++ there is no notion of the scope of a structure. There is used the notion of name spaces to distinguish identifiers. In C to refer to an identifier (member of a structure) in a name space of a structure you need to use operators . or -> to access the member. Only in this case the compiler determines that the identifier belongs to the structure name space. From the C Standard (6.2.3 Name spaces of identifiers):

1 If more than one declaration of a particular identifier is visible at any point in a translation unit, the syntactic context disambiguates uses that refer to different entities. Thus, there are separate name spaces for various categories of identifiers, as follows:

//...

— the members of structures or unions; each structure or union has a separate name space for its members (disambiguated by the type of the expression used to access the member via the . or -> operator);

— all other identifiers, called ordinary identifiers (declared in ordinary declarators or as enumeration constants).

For example in C you may write

    struct A
    {
        int x;
    } a;
    
    struct B
    {
        int y[sizeof a.x ]
    };

Or without creating an object of the structure struct A you might write

    struct A
    {
        int x;
    };

    struct B
    {
        int y[sizeof( ( (struct A * )0)->x)]
    };
 

So in your code example the compiler issues an error because it searches for an ordinary identifier x instead of referencing to the data member x of the structure.

In C++ an identifier (member of a class) is visible inside a class declaration after its point of declaration and belongs to the class scope. From the C++20 Standard (6.4.2 Point of declaration):

6 After the point of declaration of a class member, the member name can be looked up in the scope of its class.

And according to the unqualified name lookup the search of the name x of your structure s starts in the scope where the name is used that is in the class scope.

It is similar to use tag names of structures in C to declare an object of a structure type like

    struct a
    {
        int a;
    };
    
    struct a a;

In C++ you may write

struct a  
{  
    int a;  
};  
  
a a;  

If in a C program you will write an object definition as shown above in a C++ program

struct a  
{  
    int a;  
};  
  
a a;  

then again the compiler will search for an ordinary identifier a and as result will issue an error because there is no declaration of such an ordinary identifier.

To make the code to compile in C you need to introduce an ordinary identifier a by means of a typedef declaration like for example

typedef struct a a;

struct a  
{  
    int a;  
};  
  
a a;  

Pay attention to that the sizeof operator belongs to unary operators.

[expr.prim.id.general]/4:

An id-expression that denotes a non-static data member or implicit object member function of a class can only be used:

  • as part of a class member access (after any implicit transformation (see above)) in which the object expression refers to the member's class or a class derived from that class, or
  • to form a pointer to member ([expr.unary.op]), or
  • if that id-expression denotes a non-static data member and it appears in an unevaluated operand.

The operand of sizeof is an unevaluated operand and thus matches the third bullet.

本文标签: