[英]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.