In class.abstract
we can see in the Note 3 that:
An abstract class can be used only as a base class of some other class; no objects of an abstract class can be created except as subobjects of a class derived from it ([basic.def], [class.mem]).
This rules out the usage of abstract classes as subobjects and it makes sense to me (though this is just a note, which is non-normative, IIRC).
However, in class.mem
we can read that:
The type of a non-static data member shall not be an incomplete type ([basic.types]), an abstract class type ([class.abstract]), or a (possibly multi-dimensional) array thereof.
[Note 5: In particular, a class C cannot contain a non-static member of class C, but it can contain a pointer or reference to an object of class C. — end note]
(emphasis mine)
What seems strange to me is the specific wording: "non-static". Why is it explicitly stated that this refers to non-static members? I don't believe we're allowed to have static
declarations or definitions of objects of abstract types. Does the standard actually allow static
data members to be abstract and no sane compiler implements that? Or it does prohibit such uses (in that case why the distinction in static vs non-static data in the aforementioned paragraph)?
Whether or not a class is abstract is not known until the class is defined.
It has always been allowed to declare a static data member with an incomplete class type, as long as the type is complete by the time the static data member is defined. Since the type of a static data member may be incomplete on its declaration, it also follows that it might be an abstract class that the compiler doesn't yet know is abstract. For this reason, it is appropriate to defer checking of the abstractness until the static data member's definition. At the time of definition, if the type of the static data member is found to be an abstract class, then the compiler should issue a diagnostic. This was the reasoning in P0929, which added the current wording in C++20.
With non-static data members, their types are required to be complete at the time of declaration, and the declaration of the non-static data member serves as a definition. So the abstractness must be checked at that point.