P2953R4 - Forbid defaulting operator=(X&&) && WG21
Posted by u/quiet_spec_reader · 5 hr. ago

Authors: Matthew Taylor, Arthur O'Dwyer
Document: P2953R4
Date: 2026-02-22
Target: EWG
Link: wg21.link/p2953r4

C++ currently lets you write A& operator=(const A&) && = default - an explicitly defaulted copy-assignment operator that's rvalue-ref-qualified. Nobody does this on purpose. The compiler is happy to generate it, but it makes no sense: you're asking the compiler to default-implement assignment to an rvalue. This paper proposes to make it stop.

Two options on the table: the "ambitious" approach makes these declarations ill-formed outright, while the "conservative" approach makes them defaulted-as-deleted. The authors prefer ill-formed. They also sweep up a few other arcane forms - cv-qualified defaulted assignment, cv-qualified parameters in defaulted move assignment - that are currently deleted-by-default and nobody uses.

This is closely coupled with P2952 (placeholder return types for defaulted assignment), already in CWG for C++29. Without P2953, P2952 creates a subtle inconsistency where two "natural" implementations of the same operator deduce different return types.

▲ 23 points (88% upvoted) · 8 comments
sorted by: best
u/constexpr_all_the_things 38 points 4 hr. ago

I love that somewhere out there, four compilers independently decided to handle C& operator=(const C&) && = default differently, and it took until R4 of a paper to sort it out. Standards gonna standard.

u/sfinae_or_bust 14 points 3 hr. ago

The actually interesting question buried in this paper is the ill-formed vs. defaulted-as-deleted choice. Section 2.2 explains the history:

we want to continue permitting programmers to write things like the above C

That's the P0641 carve-out. You write C(const C&) = default in a class template, and if a member only has A(A&), the copy constructor quietly becomes deleted instead of making the whole template ill-formed. That matters for real code.

The paper argues the rvalue-ref-qualified case doesn't need this treatment because nobody writes B& operator=(const B&) && = default in templates. The GitHub search bears this out. But the design principle is worth watching: every time we decide "nobody does X so we can make X ill-formed instead of deleted," we're betting against future template metaprogramming creativity. The paper makes a good case that this particular bet is safe.

If you care about the wording details, the current [dcl.fct.def.default] is what's being modified.

u/daily_cpp_spelunker 21 points 2 hr. ago

Six. They searched all of GitHub, filtered out compiler test suites and LLVM forks, and found six results for rvalue-ref-qualified defaulted assignment operators. Versus 311k results for the normal forms.

I think the "will this break real code" question is settled.

u/blog_post_enjoyer 12 points 5 hr. ago

Arthur O'Dwyer finding an obscure defaulted-special-member corner case that all four major compilers handle differently is the most Arthur O'Dwyer thing that has ever happened. The man has a sixth sense for language-lawyer edge cases.

u/wording_nerd_2024 9 points 4 hr. ago

The motivation section buries the lede. The real urgency is the P2952 interaction. Without P2953, you get:

The two "natural" implementations deduce different types! This looks inconsistent.

The hand-written auto&& operator=(this C&& self, const C&) deduces C&&. The defaulted version deduces C&. Both are "doing the natural thing" and they disagree. P2953 sidesteps this by making the defaulted version unusable.

The EWG poll on merging this into P2952 was 6-for, 1-against, with nine neutrals. Not exactly a groundswell. But the sole "Against" voter on P2952's electronic poll specifically cited the lack of P2953 as their reason. So the papers are coupled whether EWG intended it or not.

u/compiler_bug_collector 7 points 3 hr. ago

The vendor divergence table in section 2.3 is worth a look. Nine test cases across Clang, GCC, MSVC, and EDG, and they disagree on at least three of them. GCC fixed one of these in 2025. MSVC and EDG still diverge on the decltype(f()) case.

This is a cleanup paper that's also documenting compiler bugs nobody knew they had.

u/just_ship_already_99 6 points 1 hr. ago

Someday I want someone to draw the complete flowchart of "when is an explicitly defaulted special member function (a) well-formed, (b) defined-as-deleted, (c) ill-formed on first declaration, or (d) ill-formed only if not on first declaration." Because after reading this paper I'm pretty sure the answer varies by compiler.

u/constexpr_all_the_things 17 points 47 minutes ago

That flowchart would be defaulted-as-deleted.