简体   繁体   中英

Does a constexpr move constructor ever make sense?

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?

This doesn't compile, since despite calling std::move on c1 , 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.

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, eg

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM