【问题标题】:Why should a type T to be emplaced at the end of a vector required to be move constructible?为什么要将类型 T 放置在需要移动构造的向量的末尾?
【发布时间】:2015-06-17 15:59:27
【问题描述】:

为什么公共构造函数(和析构函数)不能满足将T 类型的对象放置在向量的后面?以下 sn-p 格式不正确。

#include <vector>

struct Foo {
  Foo() {}
  Foo(int) {}
  ~Foo() {}

  Foo(const Foo&) = delete;
  Foo& operator=(const Foo&) = delete;
};

int main() {
  std::vector<Foo> vfoo(10);

  vfoo.emplace_back();
}

emplace_back 要求 Foo 至少是可移动构造的,并且此代码无法编译,因为移动构造函数与复制构造函数一起被删除。但我想象emplace_back 使用placement new 调用默认构造函数。

【问题讨论】:

  • 当容器被调整大小并且需要复制现有内容时,您期望会发生什么?
  • 参见en.cppreference.com/w/cpp/container/vector/emplace“参数”下的“类型要求”
  • @o11c emplaceemplace_back 是不同的东西,无论如何,问题是为什么会有这样的要求,而不是要求是什么。
  • 因为它是这样定义的。就这么简单。
  • @DavidHaim。是的。问题是关于这样做的理由。

标签: c++ c++11 move stdvector move-semantics


【解决方案1】:

在您的示例中,向量是使用 10 元素构造的。

然后你添加一个。如果capacity() 中没有更多空间,则必须分配一个新缓冲区,必须将已经存在的 10 个元素移入其中,然后追加新元素。

您会注意到上面的 move 一词——这就是您需要移动 ctor 的原因。

更重要的是,即使你没有元素,或者你仔细确保有足够的容量,编译器也不知道:移动现有元素的代码将被编译(如果不运行),你会得到你的错误。

vector 中缺少“回推,我保证已经有容量”方法。再加上“设置容量,丢弃任何现有元素”,您可以添加元素而无需移动 ctor(或复制 ctor 后备)。在emplace 被发明之前,所有添加元素都需要复制或移动:直到 C++11,所有添加元素都需要复制。缺少这两种具有奇怪语义的方法只是为了允许不可移动类型有限地使用vector,这并不奇怪。

我鼓励您编写一个具有这两个扩展(或类似的东西)的容器,并建议将其添加到 C++:它也可能有助于高性能用例(编译器在证明我确实确保容量足够时)根据我的经验)。

【讨论】:

  • 如果对象不可移动但需要重新分配,是否可以删除可移动要求,并让emplace_back 抛出? (或者如果我们不想更改,请使用名称不同的新 emplace 函数)
  • @MattMcNabb 绝对需要单独的函数。据我所知,无法准确检测出某个东西是否是MoveInsertable
  • @T.C.但是您可以精确地检测到它(但是无法确定移动实际上移动的事实,但不强制执行这似乎是合理的?)还是还有更多? (我猜分配器失败得很晚?)
  • @Yakk 你如何在普通代码中检测到它(没有内在的)?当然,您可以检测allocator_traits&lt;A&gt;::construct(m, p, rv) 在直接上下文中是否格式正确,但这并不意味着什么,因为调用内部的移动实际上会中断。然后,有些东西会在重载决议中移动失败(move ctor已删除或不存在),有些东西的移动构造函数会假装存在然后无法实例化,您只能检测前者。
猜你喜欢
  • 2018-08-22
  • 2020-04-26
  • 2010-11-30
  • 2015-07-18
  • 1970-01-01
  • 2020-06-14
  • 2022-07-15
  • 2017-01-10
  • 2017-05-30
相关资源
最近更新 更多