C++ Logo

std-discussion

Advanced search

Implicitly declared copy constructors incorrectly defined as defaulted in [class.copy.ctor]/p6?

From: Andrew Rogers <Andrew.Rogers_at_[hidden]>
Date: Tue, 2 Feb 2021 01:55:45 +0000
Hi,

Referring to N4878 / latest C++ draft:

[dcl.fct.def.default]/p5 provides the following definition of 'defaulted functions', categorising all implicitly-declared functions are defaulted functions:
"Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them (11.4.5, 11.4.7, 11.4.5.3, 11.4.6), including possibly defining them as deleted. A defaulted prospective destructor (11.4.7) that is not a destructor is defined as deleted. A defaulted special member function that is neither a prospective destructor nor an eligible special member function (11.4.4) is defined as deleted."

and I note in passing the definition of an eligible special member function from [special]/p6:

"An eligible special member function is a special member function for which:

(6.1) - the function is not deleted,
(6.2) - the associated constraints (13.5), if any, are satisfied, and
(6.3) - no special member function of the same kind is more constrained (13.5.5)."

so that an implicitly-declared special member function (which is not a template) may either be an eligible special member function (if it is not defined as deleted) or not (if it is defined as deleted).

I'd like to focus on two examples of defaulted functions, both implicitly-declared special member functions - viz. implicitly-declared default constructors ([class.default.ctor]) and implicitly-declared copy constructors ([class.copy.ctor]).

[class.default.ctor]/p1 describes how a default constructor (with no parameters) is implicitly declared in the absence of any user-declared constructor, saying that:

"A default constructor for a class X is a constructor of class X for which each parameter that is not a function parameter pack has a default argument (including the case of a constructor with no parameters). If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted (9.5)."

[class.default.ctor]/p2 then enumerates the conditions under which this defaulted function (a defaulted default constructor) may be defined as deleted (and therefore would not be an eligible special member function):

"A defaulted default constructor for class X is defined as deleted if:

(2.1) - X is a union that has a variant member with a non-trivial default constructor and no variant member of X has a default member initializer,
...
(2.8) - any potentially constructed subobject has a type with a destructor that is deleted or inaccessible from the defaulted default constructor."

and [class.default.ctor]/p4 describes when and how a defaulted default constructor will be implicitly defined (in the cases where it was not defined as deleted, and therefore is an eligible special member function):

"A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (6.3) to create an object of its class type (6.7.2), when it is needed for constant evaluation (7.7), or when it is explicitly defaulted after its first declaration. The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (11.9.3) and an empty compound-statement. If that user-written default constructor would be ill-formed, the program is ill-formed. If that user-written default constructor would satisfy the requirements of a constexpr constructor (9.2.6), the implicitly-defined default constructor is constexpr. Before the defaulted default constructor for a class is implicitly defined, all the non-user-provided default constructors for its base classes and its non-static data members are implicitly defined."

This is all in accord with the generic description of defaulted functions given in [dcl.fct.def.default]/p5.

I note in particular that the above clearly partitions implicitly-declared default constructors into those that are defined as deleted (and therefore would not be an eligible special member function) and those that are implicitly defined (and therefore are an eligible special member function).

If we turn to the second example of a category of implicitly-declared defaulted functions mentioned above, i.e. to implicitly-declared copy constructors, then we see a disparity in the spec:

As with [class.default.ctor]/p1 for the case of default constructors, [class.copy.ctor]/p6 describes the conditions under which a copy constructor will be implicitly-declared:

"If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly."

Then [class.copy.ctor]/p6 goes on to specify cases where an implicitly-declared copy constructor is defined as deleted:

"If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted;"

More analogously to [class.default.ctor]/p2, [class.copy.ctor]/p10 then provides a further list of conditions under which an implicitly-declared copy constructor will be defined as deleted:

"An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (9.5.3) if X has:

(10.1) - a potentially constructed subobject type M (or array thereof) that cannot be copied/moved because overload resolution (12.2), as applied to find M's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
...
(10.4) - for the copy constructor, a non-static data member of rvalue reference type."

For an implicitly-declared copy constructor which is not defined as deleted, and analogous to the first half of [class.default.ctor]/p4, [class.default.ctor]/p12 specifies when it will be implicitly defined:

"A copy/move constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used (6.3), when it is needed for constant evaluation (7.7), or when it is explicitly defaulted after its first declaration."

Then analogous to the first half of [class.default.ctor]/p4, [class.default.ctor]/p14 specifies how an implicitly-declared copy constructor which is not defined as deleted will be implicitly defined:

"The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members."

All this is wonderfully symmetric! However, I have not quoted the end of [class.copy.ctor]/p6, covering the case where the implicitly declared copy constructor is not defined as deleted as a result of a user-declared move constructor or move assignment operator:

"If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (9.5)."

This is saying that for all classes where there is no user-declared move constructor or move assignment operator, and where there is an implicitly declared copy constructor (since there is no user-declared copy constructor), that implicitly declared copy constructor will be "defined as defaulted".

However, in such cases any of the conditions in [class.copy.ctor]/p10 (10.1 - 10.4) may apply, in which case [class.copy.ctor]/p10 states that the implicitly declared copy constructor will be defined as deleted, rather than defined as defaulted.

As such, the spec is making two competing claims as to the status of such implicitly declared copy constructors. Though it's clear that such copy constructors should be declared as deleted, I think this contradiction should be resolved by clarification of [class.copy.ctor]/p6. In other words, it appears to me as though [class.copy.ctor]/p6 should read:

"If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is declared as defaulted (9.5)."

Is my understanding / reading correct?

Thanks, Andrew R

Received on 2021-02-01 19:55:51