Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 350
pmor Avatar asked Nov 16 '25 06:11

pmor


2 Answers

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.

like image 94
Vlad from Moscow Avatar answered Nov 18 '25 20:11

Vlad from Moscow


[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.

like image 43
cpplearner Avatar answered Nov 18 '25 18:11

cpplearner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!