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:
x has class (and a struct is a class) scope. Therefore, as I understand, sizeof will search x in the class scope first.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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With