There are many different ways to implement emplace
, and the standard is pretty lax on how implementations must do it.
Given a pointer to somewhere allocated by std::allocator_traits<allocator_type>::allocate
, the only way for a vector to construct a new object is with std::allocator_traits<allocator_type>::construct
. For the default allocator, this will call placement new.
Now if a reallocation did occur, the obvious way to emplace the new element is to call allocator_traits::construct(get_allocator(), ptr, std::forward<Args>(args...))
. This will be the equivalent of new (ptr) std::string("xyzzy")
. But note that all other elements were also move constructed to the new buffer via allocator_traits::construct(get_allocator(), ptr, std::move(old_ptr))
.
If a reallocation didn't occur, most implementations will just construct an element with value_type(std::forward<Args>(args...))
and move-assign from that (equivalent to v[0] = std::string("xyzzy")
). This is what libstdc++ does.
Alternatively, instead of move constructing v[0] = std::string("xyzzy")
, the object can be destroyed via allocator_traits::destroy
((&v[0])->~value_type()
for the default allocator), and then it can be constructed in place via allocator_traits::construct
. This seems like it would be harder to implement since special care would need to be taken to ensure that the element isn't destroyed twice if the move constructor throws, which is probably why only "few implementations" will do it.
As an aside, there is no strong exception guarantee, so move_if_noexcept
doesn't have to be used and the move constructor may always be called, even if the move constructor is not noexcept
.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…