【问题标题】:Does a constexpr move constructor ever make sense?constexpr 移动构造函数是否有意义?
【发布时间】:2017-02-20 15:27:42
【问题描述】:

拥有constexpr 移动构造函数有意义吗?

例如,考虑以下情况:

#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;
}

这不会编译,因为尽管在 c1 上调用了 std::move,编译器推断它需要使用(隐式删除的)复制构造函数,而不是移动构造函数。我不知道为什么。

但如果我从c1 中删除constexpr,那么constexpr 移动构造函数将无法使用它。

有没有办法让它工作?或者这是 constexpr 移动构造函数的一个坏例子,但是有很好的例子吗?或者,使用constexpr 移动构造函数总是错误的吗?

【问题讨论】:

  • 使用constexpr C(const C&amp;&amp; other),它从 C++14 开始编译。
  • @Jarod42 但它实际上可能不会移动任何东西,因为您无法从 const 对象移动。它只是一个不必要的复杂复制构造函数。
  • @Jarod42 嗯。确实如此。怎么会?我预计std::move(other.m_ar) 会失败,因为otherconst C&amp;&amp;(因为它可以编译,我希望它确实 移动other.m_ar,除非它做了一些偷偷摸摸的事情,例如复制other.m_ar而是)。

标签: c++ c++11 constexpr move-constructor


【解决方案1】:

原则上,移动构造函数可以与非const 对象一起使用,该对象的生命周期在常量表达式的求值期间开始:

// C++14
constexpr int f() {
    C a(/* ... */);
    C b = std::move(a);
    return 0;
}
constexpr int i = f();

类似的事情可以在 C++11 中完成,例如

constexpr C foo(C&& c) { 
    return std::move(c); 
}

constexpr int f() { 
    return foo(C()), 0; 
}

也就是说,由于常量表达式中使用的所有内容都必须是可简单破坏的,因此移动构造函数的用处相当有限。

【讨论】:

  • 有趣!我想这意味着当(如果?)在 constexpr 表达式中允许使用非平凡的析构函数时,移动构造函数可能会变得更加有用。有没有什么例子让我想到它可能对一个只有一个微不足道的析构函数的类有用?
【解决方案2】:

这不会编译,因为尽管在 c1 上调用了 std::move,编译器推断它需要使用(隐式删除的)复制构造函数

c1 的类型为 C const。当你 move() 它时,这实际上是对右值引用的强制转换,所以你会得到一个 C const&amp;&amp;。请注意,它仍然是const。当我们执行重载解析时,有三个构造函数:

C(std::array<int, 3> ); // not viable
C(C&& );                // not viable
C(C const& ) = delete;  // viable!

C const&amp;&amp; 无法绑定到 C&amp;&amp;,原因与 C const&amp; 无法绑定到 C&amp; 的原因相同。我们只剩下复制构造函数,它被隐式删除了。


拥有一个 constexpr 移动构造函数可能是有意义的——但你“移动”的对象并不能真正被移动,因为它可能是const。您可以添加一个const 移动构造函数:

constexpr C(C const&& other) : m_ar(other.m_ar) { }

这是一个美化的复制构造函数,但它允许你想要的语法。也可以只允许复制。

【讨论】:

  • 这可能意味着如果我将其参数设置为const&amp;&amp;,但保留代码std::move(other.m_ar),则上面的移动构造函数会出现偷偷摸摸的错误,因为它只会复制数组,而不是移动它。
  • 在任何情况下,目的都不是让我的语法工作,更重要的一点是如果真的有一个 constexpr move ctor 有意义。
  • @Danra 没错。除了复制之外,移动必须是其他东西。
  • constexpr 移动构造函数可用于生命周期在常量表达式求值期间开始的非常量对象:constexpr int f() { C a(/*...*/), b = std::move(a); return 0; } constexpr int i = f();
猜你喜欢
  • 2014-11-08
  • 2022-09-23
  • 2012-12-01
  • 2012-03-04
  • 1970-01-01
  • 2013-12-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多