std::vector emplace_back 可以从向量本身的元素复制构造吗?

2021-12-21 00:00:00 vector c++ c++11 stl

当使用 std::vectorpush_back 时,我可以推送向量本身的一个元素,而不必担心由于重新分配而使参数无效:

When using push_back of std::vector, I can push an element of the vector itself without fear of invalidating the argument due to reallocation:

std::vector<std::string> v = { "a", "b" };
v.push_back(v[0]); // This is ok even if v.capacity() == 2 before this call.

然而,当使用 emplace_back 时,std::vector 将参数转发给 std::string 的构造函数,从而发生复制构造在向量中的位置.这让我怀疑向量的重新分配发生在复制构造新字符串之前(否则它不会就地分配),从而在使用前使参数无效.

However, when using emplace_back, std::vector forwards the argument to the constructor of std::string so that copy construction happens in place in the vector. This makes me suspect that reallocation of the vector happens before the new string is copy constructed (otherwise it would not be allocated in place), thus invalidating the argument before use.

这是否意味着我不能使用 emplace_back 添加向量本身的元素,或者我们是否有某种保证以防止重新分配,类似于 push_back?

Does this mean that I cannot add an element of the vector itself with emplace_back, or do we have some kind of guarantee in case of reallocation, similar to push_back?

在代码中:

std::vector<std::string> v = { "a", "b" };
v.emplace_back(v[0]); // Is this valid, even if v.capacity() == 2 before this call?

推荐答案

emplace_back 出于同样的原因需要安全 push_back 是安全的;指针和引用的失效仅在修改方法调用返回后生效.

emplace_back is required to be safe for the same reason push_back is required to be safe; invalidation of pointers and references only has effect once the modifying method call returns.

实际上,这意味着 emplace_back 执行重新分配需要按以下顺序进行(忽略错误处理):

In practice, this means that emplace_back performing a reallocation is required to proceed in the following order (ignoring error handling):

  1. 分配新容量
  2. 在新数据段的末尾放置-构造新元素
  3. 将现有元素移动到新的数据段中
  4. 销毁并释放旧数据段

在 这个 reddit 线程 STL 承认 VC11 失败support v.emplace_back(v[0]) as a bug,所以你一定要检查你的库是否支持这种用法,而不是想当然.

At this reddit thread STL acknowledges failure of VC11 to support v.emplace_back(v[0]) as a bug, so you should definitely check whether your library supports this usage and not take it for granted.

请注意,标准明确禁止某些形式的自插入;例如在 [sequence.reqmts] 段 4 表 100 a.insert(p,i,j) 有先决条件i 和 j 不是 a" 的迭代器.

Note that some forms of self-insertion are specifically prohibited by the Standard; for example in [sequence.reqmts] paragraph 4 Table 100 a.insert(p,i,j) has the prerequisite "i and j are not iterators into a".

相关文章