1. Changelog
-
R5:
-
Change paper title, per CWG request.
-
P2953R4 was seen by EWG in Croydon, "ambitious" approach voted through to CWG.
-
Retarget CWG, update § 4 Straw poll results.
-
Remove unused "conservative" design alternative and proposed polls.
-
Add Annex C entry to § 5 Proposed wording.
-
Add discussion of feature test macro to § 2 Motivation and proposal.
-
CWG wording review.
-
-
R4:
-
Implementation of ambitious approach, deployment considerations.
-
-
R3:
-
Expand motivation: [P3834] also has to solve this problem.
-
Update references to standard versions and features that didn’t make C++26.
-
Propose the "ambitious" wording by default.
-
-
R2 (pre-Sofia):
-
Add poll results from the EWG telecon of 2025-01-08.
-
Add electronic poll results after the EWG telecon of 2025-01-08.
-
Update the vendor-divergence table: GCC has fixed #116162.
-
Replace the word "signature" with "declaration" in the motivation section. ("Signature" doesn’t include return type.)
-
Add a missing clause to the "ambitious" wording. (Thanks, Jens Maurer.)
-
-
R1:
-
Propose an "ambitious" wording as well as the "conservative" surgery.
-
2. Motivation and proposal
Currently, [dcl.fct.def.default]/2.5 permits an explicitly defaulted special member function to differ from the implicit one by adding ref-qualifiers, but not cv-qualifiers.
For example, the declaration
is forbidden
because it is additionally const-qualified, and also because its return type differs
from the implicitly-defaulted . This might be
considered unfortunate, because it’s
a reasonable signature for a const-assignable proxy-reference type.
But programmers aren’t clamoring for that declaration to be supported, so we do not propose it here.
Our concern is that the unrealistic declaration
is
permitted! This has several minor drawbacks:
-
The possibility of these unrealistic declarations makes C++ harder to understand.
-
Additional papers in this space (such as [P3834]) must consider adding new, similarly implausible signatures to the language to remain consistent with this oddity. EWG previously found consensus in Kona to explore this space [Kona25], and any such paper will encounter this.
-
The quirky interaction with [CWG2586] and [P2952] discussed in the next subsection.
-
The wording to permit these declarations is at least a tiny bit more complicated than if they weren’t permitted.
To eliminate these drawbacks, we propose that an explicitly defaulted copy/move assignment operator should not be allowed to add an rvalue ref-qualifier to the type it would have had if implicitly defaulted.
struct C { C & operator = ( const C & ) && = default ; // C++26: Well-formed // Proposed: Ill-formed }; struct D { D & operator = ( this D && self , const C & ) = default ; // C++26: Well-formed // Proposed: Ill-formed };
This proposal applies only to explicitly defaulted , and only when the
object parameter itself is rvalue-ref-qualified. We propose that it remain legal to
write rvalue-ref-qualified functions by hand;
the compiler simply shouldn’t
assume it knows how to default one.
We propose that rvalue-ref-qualified defaulted assignment operators, along with select other problematic signatures, be ill-formed. We previously additionally proposed a "conservative" design alternative which simply made the rvalue-ref-qualified case defaulted-as-deleted, but EWG found consensus for them to be ill-formed instead.
When reviewing existing wording in [dcl.fct.def.default]/2.6, we noticed that this current wording also permits cv-qualified assignment operators, and move assignment operators which accept a cv-qualified non-object parameter, to be defaulted-as-deleted. These signatures are similarly arcane, we expect that no users make use of them, and these signatures don’t help with template programming either (see § 2.2 "Deleted" versus "ill-formed"). Therefore we propose making all these signatures ill-formed too.
In summary, our preferred wording makes all of the below declarations ill-formed:
//rvalue-ref-qualified overload (permitted in '26) foo & operator = ( const foo & ) && = default ; foo & operator = ( foo && ) && = default ; //cv-qualified overload (deleted in '26) foo & operator = ( const foo & ) const = default ; foo & operator = ( foo && ) const = default ; //cv-qualified rvalue reference parameter (deleted in '26) foo & operator = ( const foo && ) = default ;
We intentionally decide not to add a feature test macro for this feature, as we believe that it would not serve a reasonable purpose.
2.1. Interaction with P2952
[CWG2586] (adopted for C++23)
permits to have an explicit object parameter.
[P2952] (currently in CWG for C++29) proposes
that defaulted overloads should (also) be
allowed to have a placeholder return type.
If C++29 gets P2952 without P2953, then we’ll have:
struct C { auto && operator = ( this C && self , const C & ) { return self ; } // C++26: OK, still deduces C&& auto && operator = ( this C && self , const C & ) = default ; // C++26: Ill-formed, return type contains auto // C++29 after P2952: OK, deduces C& // Proposed (preferred): Ill-formed, object parameter is not C& // Proposed (conservative): Deleted, object parameter is not C& };
The first, non-defaulted, operator "does the natural thing" by returning its left-hand operand,
and deduces . The second operator also "does
the natural thing" by being defaulted; but
it deduces , just like any other defaulted
assignment operator.
The two "natural" implementations deduce different types! This looks inconsistent.
If we adopt P2953 alongside P2952, then the second will go back to being unusable,
which reduces the perception of inconsistency.
| C++26 | P2952 | |
|---|---|---|
| C++26 | /ill-formed
| /
|
| P2953 | /ill-formed
| /ill-formed
|
2.2. "Deleted" versus "ill-formed"
(See also [P2952] §3.2 "Defaulted as deleted".)
[dcl.fct.def.default]/2.6 goes out of its way to make many explicitly defaulted constructors, assignment operators, and comparison operators "defaulted as deleted," rather than ill-formed. This was done by [P0641] (resolving [CWG1331]), in order to support class templates with "canonically spelled" defaulted declarations:
struct A { // Permitted by (2.4) A ( A & ) = default ; A & operator = ( A & ) = default ; }; template < class T > struct C { T t_ ; explicit C (); // Permitted, but defaulted-as-deleted, by (2.6), since P0641 C ( const C & ) = default ; C & operator = ( const C & ) = default ; }; C < A > ca ; // OK
There is similar wording in [class.spaceship]
and [class.eq]. We don’t want to interfere with
these use-cases; that is, we want to continue permitting programmers to write things like the above .
We consider the carve-out for the copy assignment operator of in the above example
sensible and do not intend to interfere with it. However, as nobody ever writes
we do not need to add any new carveouts.
2.3. Existing corner cases
There is vendor divergence in some corner cases. Here is a table of the divergences we found, plus our opinion as to the currently conforming behavior, and our proposed behavior. Red cells in this table indicate non-conformance among vendors today.
| URL | Code | Clang | GCC | MSVC | EDG | Correct | Proposed |
|---|---|---|---|---|---|---|---|
| link |
| ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| link |
| deleted | ✓ | ✗ | deleted | deleted | ✗ |
| link |
| deleted | ✓ | ✗ | deleted | deleted | ✗ |
| link |
| ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| link |
| ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| link |
| ✓ | ✗ | ✗ | ✗ | ✓ | ✓ |
| link |
| deleted | deleted | deleted | deleted | deleted | deleted |
| link |
| deleted | deleted | ✗ | deleted | deleted | deleted |
| link |
| ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
3. Implementation experience
Arthur has implemented both § 5 Proposed wording and our previously-proposed "conservative" design in forks of Clang, and used them to compile both LLVM/Clang/libc++ and another large C++17 codebase. Naturally, neither patch caused any problems except in the relevant parts of Clang’s own test suite. Matthew has also grepped a large C++20 codebase and found that it does not contain any of the signatures we seek to make ill-formed in § 5 Proposed wording.
We have also searched for any real-world use of these operator overloads to assess deployment. Searching GitHub for rvalue-ref-qualified defaulted assignment currently yields around 1.5k results. The vast majority of these are compiler tests and test files from LLVM forks. Refining the search to remove test files and forks of Clang and LLVM reduces the count down to 6.
Running a similarly refined search for cv-qualified assignment yields 10 results, all of which comment out the overload in question.
Finally, we ran a search for cv-qualified parameters in defaulted move assignment and got 36 results.
Note that these searches will undercount slightly, as GitHub search imposes a limit on the greediness of regex used. It is possible that there are additional examples of these signatures on GitHub which use a slightly different series of whitespace, which would not be counted in this search. However, we can get a very approximate measure on this by comparing against a similarly-constrained search for the "canonical" defaulted assignment operator forms, which yields 311k results. As such, we are confident that the breakage from making these signatures ill-formed would be insignificant.
4. Straw poll results
Arthur O’Dwyer presented P2592R1 (not P2953, but P2952) in the EWG telecon of 2025-01-08. In addition to the vote forwarding P2952 to CWG, the following straw poll relevant to P2953 was taken. The result was interpreted as "no consensus"; but the numbers (6 for, 1 against) are still a strong signal that EWG was favorably inclined toward P2953 in general.
| SF | F | N | A | SA | |
|---|---|---|---|---|---|
| EWG prefers this paper contains the change in P2953
(banning explicitly defaulted operator= with rvalue ref-qualifier). [Chair: This means EWG wants to see this paper again.] | 2 | 4 | 9 | 1 | 0 |
P2952R1 went to electronic polling, and passed 3–6–1–1–0. The sole voter "Against" P2952’s adoption gave as their rationale (paraphrased): "Without P2953 these changes add a corner case to the language. We should prevent that corner case by applying P2953 at the same time as P2952."
Matthew presented P2953R4 to EWG at the Croydon meeting on 2026-03-25. The following polls were taken:
| SF | F | N | A | SA | |
|---|---|---|---|---|---|
| P2953R4: EWG prefers §5 Proposed wording
(ambitious approach), which makes X& operator=(X&&) const = default; and X& operator=(const X&&) = default; ill-formed. | 2 | 13 | 8 | 1 | 0 |
| Forward P2953R4 (proposed wording in §5) to CWG for C++29 | 4 | 21 | 0 | 0 | 0 |
Both of which were called as consensus in favor.
5. Proposed wording
DRAFTING NOTE: The intent of this "ambitious" wording is to lock down the permissible types of defaultable member functions as much as possible, and make errors as eager as possible, except in the cases covered by § 2.2 "Deleted" versus "ill-formed", which we want to keep working, i.e., "defaulted as deleted."
DRAFTING NOTE: The only defaultable special member functions are default constructors, copy/move constructors, copy/move assignment operators, and destructors. Of these, only the assignment operators can ever be cvref-qualified.
5.1. [dcl.fct.def.default]
DRAFTING NOTE:
The new (2.5) ensures that
remains defined-as-deleted, and
remains ill-formed (not defined-as-deleted, despite that it matches the pattern in (2.5); because
it also differs in a second way).
DRAFTING NOTE:
Basically all of this wording is concerned specifically with copy/move assignment operators,
so it might be nice to move it out of [dcl.fct.def.default] and into [class.copy.assign].
Also note that right now a difference in -ness is handled
explicitly by [dcl.fct.def.default]
for special member functions but only by omission-and-note in [class.compare]
for comparison operators.
Modify [dcl.fct.def.default] as follows:
1․ A function definition whose function-body is of the formis called an explicitly-defaulted definition. A function that is explicitly defaulted shall= default ;
(1.1) be a special member function or a comparison operator function ([over.binary]), and
(1.2) not have default arguments.
2․ The program is ill-formed if a
An explicitly defaulted special member function1 of a classF C is allowed todiffers from the corresponding special member function2 that would have been implicitly declared other than as follows:F
(2.1) if
1 is an assignment operator,F it may have the & ref-qualifier;1 andF 2 may have differing ref-qualifiersF (2.2) if
2 is an assignment operator (which has an implicit object parameter of type “lvalue reference to C”),F 1 mayF be an explicit object member function whosehave an explicit object parameterisof type(possibly different)“lvalue reference to C”, in which case the type of1 would differ from the type ofF 2 in that the type ofF 1 has an additional parameter;F (2.3)
1 andF 2 may have differing exception specifications;F and(2.4) if
2 has a non-object parameter of typeF "lvalue reference toconst C & ", the corresponding non-object parameter ofconst C 1 may be of typeF "lvalue reference toC & "; andC - (2.5) if
2 has a non-object parameter of type "lvalue reference toF ", the corresponding non-object parameter ofC 1 may be of type "lvalue reference toF "; in this case only,const C 1 is defined as deleted if it is explicitly defaulted on its first declaration and the program is ill-formed otherwise.F
If the type of1 differs from the type ofF 2 in a way other than as allowed by the preceding rules, thenF
(2.5) if1 is an assignment operator, and the return type ofF 1 differs from the return type ofF 2 orF 1’s non-object parameter type is not a reference, the program is ill-formed;F (2.6) otherwise, if1 is explicitly defaulted on its first declaration, it is defined as deleted;F (2.7) otherwise,the program is ill-formed.[...]
5.2. Annex C
Add a clause to Annex C under [dcl]:
1Affected subclause: [dcl.fct.def.default]
Change: It is no longer valid for explicitly defaulted assignment operators to have a cv-qualifier-seq or && ref-qualifier, or for an explicitly defaulted move assignment operator to have a parameter of type is "reference to T" where T is cv-qualified.
Rationale: Removal of rarely-used and confusing feature.
Effect on original feature: A valid C++26 program which uses these features on an explicitly defaulted assignment operator is ill-formed.
Example:
struct S {
S & operator = ( const S & ) && = default ; //ill-formed; previously well-formed };
struct T {
T & operator = ( const T & ) const = default ; //ill-formed; previously well-formed but deleted };
struct U {
U & operator = ( const U && ) = default ; //ill-formed; previously well-formed but deleted };