【问题标题】:How do I have a const member move-only object without making my object copyable?如何在不使我的对象可复制的情况下拥有一个 const 成员仅移动对象?
【发布时间】:2021-01-22 20:40:01
【问题描述】:
#include<algorithm>
#include<vector>
#include<memory>

class Foo {
 public:
  Foo();

#if 1
  // Destructor for rule of 5.
  ~Foo(){}

  // Move constructor yes.
  Foo(Foo&&) noexcept = default;

  // Move assignment probably won't actually be created because const member variable.
  // Do I need it though? Why would vector need to move-assign?
  Foo& operator=(Foo&&) noexcept = default;

  // Copy no.
  Foo(const Foo&) = delete;
  Foo& operator=(const Foo&) = delete;
#endif

 protected:
  // It works if non-const. Broken if const.
  const std::unique_ptr<int> ptr;
};

int main()
{
  std::vector<Foo> bar;
  bar.reserve(1);
}

我收到错误(使用 GCC):

static assertion failed: result type must be constructible from value type of input range

但是为什么呢?

在我看来,默认构造函数和移动构造函数都应该没问题。这是万一吗?我 const_cast 去掉 const 并擦除中间元素,vector 可能会尝试移动分配以“碎片整理”向量? (而如果它试图破坏+移动构造,那会起作用)

在上面的片段中,unique_ptr 代表我自己的只移动类型,所以这个问题与指针无关。

【问题讨论】:

  • 您希望指针const 还是希望它指向 的对象为const?在后一种情况下,只需:std::unique_ptr&lt;const int&gt; ptr;
  • 如果unique_ptr 是常量,则不能移动它,因为这会将其指针重置为nullptr
  • ""// 5 规则的析构函数。"对规则 5 的错误解释。“如果你实现 1,那么你需要实现所有 5”是一种简化。整个故事是这样的:如果你觉得需要写 5 个中的 1 个,那么你的班级很可能拥有并管理一个资源。如果您的班级负责手动管理资源,那么您需要实现所有 5 个。在您的情况下,您的班级不管理任何资源,因此您不需要编写析构函数。实际上建议不要写析构函数。
  • 你不能移动 const 的东西。移动构造函数的默认实现将不起作用,但您可以实现自己的移动构造函数,并复制无法移动的成员并移动其他可移动的成员。
  • @TedLyngmo:就像我说的,这是一个 unique_ptr 代表实际对象的示例。这与指针无关。我知道你所说的区别。

标签: c++ vector stl move-semantics


【解决方案1】:

如何在不使我的对象可复制的情况下拥有一个 const 成员只移动对象?

拥有一个只能移动的成员对象会使您的类隐式不可复制,因此没有什么特别需要做的事情来实现这一点。


我得到了错误

但是为什么呢?

因为你的类不满足 MoveInsertable 的要求。这是因为它不可移动构造。

在我看来,默认构造函数和移动构造函数都应该没问题。

移动构造函数不好,因为成员不可移动。该成员是不可移动的,因为它既不可复制又是 const。

结论:你可以拥有这样的类,但它不能与std::vector::reserve 一起使用,因为它既不可复制也不可移动。

【讨论】:

  • 我明白了。所以要清楚,问题是我试图移动 from 一个 const 成员,而不是目标成员(至少在移动构造函数的情况下)是 const?
  • @Thomas 是的。您正在尝试更改 const 成员(在 moved-from 对象中)。构造对象的const 成员可以被初始化,但永远不会被分配(或移出)。非staticconst 的成员真的很痛苦。我很少发现它们有什么用处。
  • @Thomas 在移动分配中,两者都是问题。初始化一个 const 对象(这就是你在构造函数中所做的)不是问题,但源代码的 constness 仍然是。
猜你喜欢
  • 2021-08-11
  • 2021-07-21
  • 1970-01-01
  • 2017-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多