Reputation: 9926
Does it ever make sense to have a constexpr
move constructor?
For example, consider the following:
#include <array>
class C
{
public:
constexpr C(std::array<int, 3> ar) : m_ar{ar} {}
constexpr C(C&& other) : m_ar{std::move(other.m_ar)} { }
private:
std::array<int, 3> m_ar;
};
int main()
{
constexpr C c1 {{{1, 2, 3}}};
constexpr C c2{std::move(c1)};
return 0;
}
This doesn't compile, since despite calling std::move
on c1
, the compiler deduces it needs to use the (implicitly deleted) copy constructor, not the move constructor. I'm not sure why.
But if I remove the constexpr
from c1
, then it becomes unusable by the constexpr
move constructor.
Is there any way to get this to work? Or is this a bad example for a constexpr
move constructor, but there are good examples? Or, is it just always wrong to have a constexpr
move constructor?
Upvotes: 8
Views: 2267
Reputation: 137385
In principle, a move constructor can be used with a non-const
object whose lifetime started during the evaluation of the constant expression:
// C++14
constexpr int f() {
C a(/* ... */);
C b = std::move(a);
return 0;
}
constexpr int i = f();
Similar things can be done in C++11, e.g.
constexpr C foo(C&& c) {
return std::move(c);
}
constexpr int f() {
return foo(C()), 0;
}
That said, since everything used in a constant expression is required to be trivially destructible, the usefulness of a move constructor is rather limited.
Upvotes: 3
Reputation: 303576
This doesn't compile, since despite calling
std::move
onc1
, the compiler deduces it needs to use the (implicitly deleted) copy constructor
c1
is of type C const
. When you move()
it, that's really a cast to rvalue reference, so you get a C const&&
. Note that it's still const
. When we perform overload resolution, there are three constructors:
C(std::array<int, 3> ); // not viable
C(C&& ); // not viable
C(C const& ) = delete; // viable!
C const&&
can't bind to C&&
for the same reason that C const&
can't bind to C&
. We're left with just the copy constructor, which is implicitly deleted.
Having a constexpr move constructor could make sense - but the object you're "moving" from can't really be moved from, because it's presumably const
. You could add a const
move constructor:
constexpr C(C const&& other) : m_ar(other.m_ar) { }
This is a glorified copy constructor, but it allows the syntax you want. May as well just allow copying.
Upvotes: 2