I see a similar question Default move constructor taking a const parameter, which is 8 years old, with the answer No.
But at the same time, a slightly modified program with the constructor defaulted after the class definition:
struct A {
A(const A&&);
};
A::A(const A&&) = default;
is accepted by EDG 6.7 and recently released GCC 15.1. Online demo: https://gcc.godbolt.org/z/E4qT3sTEq
And even more complex example appears to work correctly with these two compilers:
struct A {
int i;
constexpr A(int v) : i(v) {}
constexpr A(const A&&);
};
constexpr int f() {
A a(1);
A b = static_cast<const A&&>( a );
return b.i;
}
constexpr A::A(const A&&) = default;
static_assert( f() == 1 );
But MSVC still dislikes it:
error C2610: 'A::A(const A &&)': is not a special member function or comparison operator which can be defaulted
<source>(13): note: the argument must be a non-const rvalue reference
as well as Clang:
error: the parameter for an explicitly-defaulted move constructor may not be const
Online demo: https://gcc.godbolt.org/z/6W9W865vG
Has anything changed during the last 8 years in this relation? Which implementation is correct now?
The rule in [dcl.fct.def.default] is:
An explicitly defaulted special member function
F1
is allowed to differ from the corresponding special member functionF2
that would have been implicitly declared, as follows:
F1
andF2
may have differing ref-qualifiers;- if
F2
has an implicit object parameter of type “reference toC
”,F1
may be an explicit object member function whose explicit object parameter is of (possibly different) type “reference toC
”, in which case the type ofF1
would differ from the type ofF2
in that the type ofF1
has an additional parameter;F1
andF2
may have differing exception specifications; and- if
F2
has a non-object parameter of typeconst C&
, the corresponding non-object parameter ofF1
may be of typeC&
.If the type of
F1
differs from the type ofF2
in a way other than as allowed by the preceding rules, then:
- if
F1
is an assignment operator, [...]- otherwise, if
F1
is explicitly defaulted on its first declaration, it is defined as deleted;- otherwise, the program is ill-formed.
The implicitly declared move constructor would look like A(A&&)
. A(A const&&)
differs from that in a way that doesn't match any of those four exceptions (there is a special case for defining a copy constructor as C(C&) = default;
, that's the fourth bullet, but nothing for move constructors).
So we fall into the second rule. Had this move constructor been defaulted on its first declaration, it would have been defined as deleted. But in the OP, it's defaulted out of line — so this should be ill-formed.
In C++17 (as in C++14), this still would have been ill-formed (even if the function had been defaulted on its first declaration). So while the rules around defaulting have been relaxed a bit, those relaxations would not affect this example.