[英]What does it mean that the emplacement function adds a new element to a container via construction in place istead of via assignment?
在Effective Modern C++的第 41 项中,Scott Meyers 提到了这种差异及其对插入效率的影响。
我对此有一些疑问,但在提出问题之前,我需要了解这两种添加元素的方式之间的区别是什么。
考虑书中的代码示例:
std::vector<std::string> vs;
// adding some elements to vs
vs.emplace(v.begin(), "xyzzy");
很明显,在// adding some elements to vs
之后,可能是
vs.capacity() == vs.size()
,或vs.capacity() > vs.size()
。相应地,
vs
必须重新分配,并且 vs 中的所有预先存在的元素(那些名为vs
vs[0]
, vs[1]
,...在重新分配发生之前)必须移动构造到新的 memory 位置(新位置的vs[1]
, vs[2]
, ...)vs
必须vs.resize(vs.size() + 1)
并且所有预先存在的元素必须移动分配给下一个索引,显然是向后处理,从最后一个元素到第一个元素。 (显然我提到了移动操作,因为std::string
提供了noexcept
移动操作。如果不是这种情况,那么上面的场景略有不同,将使用复制操作。)
然而,我的问题的症结所在,就在上面的代码之后,书上写着(我的斜体)
[…] 很少有实现会将添加的
std::string
构造到由vs[0]
占用的 memory中。 相反,他们会将值移动分配到位。 […]
在完成房间以容纳新元素之后,这两种情况是什么?
emplace
通过移动赋值添加元素,则意味着它确实v[0] = std::string{strarg};
其中strarg == "xyzzy"
,对吗?v[0]
占用的元素的另一种情况是什么? 这是一个new
的展示位置吗? 它会是什么样子? 我想它应该类似于Placement new here部分的代码块,但我不确定在这种情况下它会是什么样子。 有许多不同的方式来实现emplace
,并且标准对于实现必须如何做到这一点非常松懈。
给定一个指向由std::allocator_traits<allocator_type>::allocate
的位置的指针,向量构造新 object 的唯一方法是使用std::allocator_traits<allocator_type>::construct
。 对于默认分配器,这将调用placement new。
现在,如果确实发生了重新分配,放置新元素的明显方法是调用allocator_traits::construct(get_allocator(), ptr, std::forward<Args>(args...))
。 这将等效于new (ptr) std::string("xyzzy")
。 但请注意,所有其他元素也通过allocator_traits::construct(get_allocator(), ptr, std::move(old_ptr))
移动构造到新缓冲区。
如果没有发生重新分配,大多数实现只会使用value_type(std::forward<Args>(args...))
构造一个元素并从中移动分配(相当于v[0] = std::string("xyzzy")
)。 这就是 libstdc++ 所做的。
或者,代替移动构造v[0] = std::string("xyzzy")
, object 可以通过allocator_traits::destroy
( (&v[0])->~value_type()
用于默认分配器)销毁,然后可以通过allocator_traits::construct
就地构造它。 这似乎更难实现,因为如果移动构造函数抛出,需要特别注意确保元素不会被破坏两次,这可能就是为什么只有“少数实现”会这样做。
顺便说一句,没有强大的异常保证,因此不一定要使用move_if_noexcept
,并且可以始终调用移动构造函数,即使移动构造函数不是noexcept
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.