Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve certain definitions in [class.mem.general] #5223

Open
xmh0511 opened this issue Jan 21, 2022 · 8 comments
Open

Improve certain definitions in [class.mem.general] #5223

xmh0511 opened this issue Jan 21, 2022 · 8 comments

Comments

@xmh0511
Copy link
Contributor

xmh0511 commented Jan 21, 2022

NO.1

A direct member of a class X is a member of X that was first declared within the member-specification of X
Consider this example

struct X{
  struct Nested{
     int a;
  };
};

Since the class-specifier of Nested is within the member-specification of X, thus every member-specification within Nested is also within the member-specification of X and also first be declared. That is not intent of the rule, any intervening class-specifier should abort the application of the definition. So, the improvement for this rule is that:

A direct member of a class X is a member of X that was first declared within the member-specification of X and the innermost class-specifier enclosing that member-specification is X's.

NO.2

[class.mem.general] p21

If T is the name of a class, then each of the following shall have a name different from T:

  • every static data member of class T;
  • every member function of class T;
  • every member of class T that is itself a type;
  • every member template of class T;
  • every enumerator of every member of class T that is an unscoped enumerated type; and
  • every member of every anonymous union that is a member of class T.

Since [class.derived.general] p2 says

Members of a base class are also members of the derived class.

Obviously, the intent of the above bullets should refer to the direct member of class T.

NO.3

Consider the third bullet in the above list

every member of class T that is itself a type;

How about injected-class-name?

The class-name is also bound in the scope of the class (template) itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name.

[basic.scope.pdecl] p8

The locus of an injected-class-name declaration ([class.pre]) is immediately following the opening brace of the class definition.

[class.mem.general] p1

The member-specification in a class definition declares the full set of members of the class; no member can be added elsewhere.

So, the injected-class-name is a member, and the declaration associated with it has a locus, which admits it should have a declaration. Furthermore, no member can be added elsewhere, thus, it should act as a member declaration, as it is a member. Hence, the third bullet should exclude this special case. Moreover, an additional improvement to the third bullet is that

every member of class T that itself denotes a type

Since we have a clear definition regarding denote an entity in [basic.pre] p5

An entity E is denoted by the name (if any) that is introduced by a declaration of E or by a typedef-name introduced by a declaration specifying E.

The improvement will cover two cases that either the member declaration itself introduces a type or the member declaration itself introduces a typedef-name to denote the type instead of introducing a new type.

@jensmaurer
Copy link
Member

jensmaurer commented Jan 21, 2022

ad 1) "a" is not a member of "X", so it can't be a direct member. "A direct member of a class X is a member of X that (further restriction)." Something that is not a member can't be a direct member.

ad 2) Yes, we probably have quite a bit of confusion around direct member vs. member.

ad 3) No, the injected-class-name is not a member; it's just a name with special semantics bound in the scope of the class. Just for the narrow purpose of access checking is it considered a public member. The injected-class-name thus is not in scope for the "shall have a name different from T" rules.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jan 22, 2022

@jensmaurer For the first issue. [class.mem.general] p1 states

The member-specification in a class definition declares the full set of members of the class; no member can be added elsewhere.

The member-specification that comprises the member-declaration int a; is in the definition of X, although there is an intervening definition of Nested. Hence, I would say that int a; declares a member of X. Although, based on consensus, a is not a member of X, however, we just talk about the interpretation purely based on the above provision. There is room for improvement for this provision.

The member-specification in a class definition declares the full set of members of the class if and only if the innermost enclosing class-specifier of the member-specification is that of X; no member can be added elsewhere.

It may eliminate the above confusion: what the extent in applies to.


For the third issue. Since we admit there is a declaration associated with the nested-class-name, as per [basic.scope.pdecl] p8. And we also admit that: The class-name is also bound in the scope of the class (template) itself;. Based on these premises, and what [basic.scope.scope] p2 states

Unless otherwise specified:

  • [...]
  • A declaration inhabits the immediate scope at its locus
  • A declaration's target scope is the scope it inhabits.
  • Any names (re)introduced by a declaration are bound to it in its target scope.

Since we never specify the otherwise rules for the declaration of an injected-class-name, [basic.scope.scope] p2 should be default applied to that declaration. I cannot figure out any declaration that can have these functions except a member-declaration.

@jensmaurer
Copy link
Member

First issue: I don't think your change is an improvement at all. I do agree that the topic of "members of a class" could use a larger rephrasing along the lines of "the direct members of a class are those entities introduced by declarations in the member-specification whose target scope is the class; the members of a class are the direct members of a class plus all members of all direct base classes".

Third issue: The implicit declaration of an injected-class-name is a "declaration" per basic.pre p5.14. The declaration of the injected-class-name is not lexically part of the member-specification of a class, so it is not a member. Yet, the name is bound to the class scope and is visible to member name lookup. I think this reading has the right semantic outcome.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jan 22, 2022

For the first issue, I think your proposal wording is good and consistent with the approach of P1989(i.e., phrase the things by using the target scope).

The declaration of the injected-class-name is not lexically part of the member-specification of a class, so it is not a member.

An implicitly-declared constructor is also not a lexically part of the member-specification of a class, but it is a member. Since we admit that the injected-class-name does have a corresponding declaration, if we don't give an extra regulation, [basic.scope.scope] p2 can be applied to the declaration.

Maybe, we can explicitly clarify the hazy.

The implicit declaration of an injected-class-name does not inhabit a class scope; its target scope is the class scope associated with the class-name.

According to [basic.scope.scope] p2.2 and [basic.scope.scope] p2.5, the above sentence might clarify two things:

  • implies that the declaration cannot be a member-declaration, the member-declaration does inhabit a class scope.
  • the name is bound to the class scope since any names (re)introduced by a declaration are bound to it in its target scope. it is consistent with [class.pre] p2.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jan 22, 2022

NO.4

The class-name is also bound in the scope of the class (template) itself; this is known as the injected-class-name.

Since injected-class-name is a name, instead, and a class-name can be either an identifier or a simple-template-id, and only the former is a name. Hence, we may change the above sentence to

The class-name that is an identifier is also bound in the scope of the class (template) itself; this is known as the injected-class-name.

@jensmaurer
Copy link
Member

ad 4) If the class-name is a simple-template-id, we bind the template-name of the simple-template-id.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jan 22, 2022

ad 4) If the class-name is a simple-template-id, we bind the template-name of the simple-template-id.

Since [class.pre] p1 states

A class declaration where the class-name in the class-head-name is a simple-template-id shall be an explicit specialization ([temp.expl.spec]) or a partial specialization ([temp.spec.partial]).

In [temp.expl.spec], p4 says

An explicit specialization does not introduce a name ([basic.scope.scope]).

In [temp.spec.partial], p7 says

Partial specialization declarations do not introduce a name.

Hence, only the class or primary class template whose class-name is an identifier introduces a name, and it is reasonable that such a name is also the injected-class-name, I think.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jan 22, 2022

NO.5

class-name and injected-class-name are introduced by different declarations. Assume the class-specifier is a declaration C while the implicit declaration of injected-class-name is I. [basic.scope.scope] p2 says

Any names (re)introduced by a declaration are bound to it in its target scope.

Hence, we can get two conclusions:

  • class-name is bound to C in C's target scope.
  • injected-class-name is bound to I in I's target scope.

Whereas, [class.pre] p2 says

The class-name is also bound in the scope of the class (template) itself; this is known as the injected-class-name.

This is contradictory since these two names are bound to their respective declarations, class-name ≠ injected-class-name. Based on this, we cannot say that class-name ... is known as the injected-class-name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants