Skip to content

Commit 757933e

Browse files
committed
Formulate vector::emplace and insert in terms of relocation
1 parent 21f29c4 commit 757933e

File tree

2 files changed

+87
-65
lines changed

2 files changed

+87
-65
lines changed

libcxx/include/__memory/temp_value.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@
2121

2222
_LIBCPP_BEGIN_NAMESPACE_STD
2323

24+
template <class _Tp>
25+
struct __temporary_emplace_value {
26+
union {
27+
_Tp __value_;
28+
};
29+
30+
template <class _Allocator, class... _Args>
31+
_LIBCPP_HIDE_FROM_ABI
32+
_LIBCPP_CONSTEXPR_SINCE_CXX20 explicit __temporary_emplace_value(_Allocator& __alloc, _Args&&... __args) {
33+
allocator_traits<_Allocator>::construct(__alloc, std::addressof(__value_), std::forward<_Args>(__args)...);
34+
}
35+
36+
// Don't destroy anything, since we assume that the value gets relocated by whoever uses this type
37+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__temporary_emplace_value() {}
38+
39+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp* __address() { return std::addressof(__value_); }
40+
};
41+
2442
template <class _Tp, class _Alloc>
2543
struct __temp_value {
2644
typedef allocator_traits<_Alloc> _Traits;

libcxx/include/__vector/vector.h

Lines changed: 69 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -472,11 +472,77 @@ class _LIBCPP_TEMPLATE_VIS vector {
472472
this->__destruct_at_end(this->__end_ - 1);
473473
}
474474

475-
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, const_reference __x);
475+
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, const_reference __x) {
476+
return emplace(std::move(__position), __x);
477+
}
476478

477-
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, value_type&& __x);
479+
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, value_type&& __x) {
480+
return emplace(std::move(__position), std::move(__x));
481+
}
478482
template <class... _Args>
479-
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator emplace(const_iterator __position, _Args&&... __args);
483+
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator emplace(const_iterator __cposition, _Args&&... __args) {
484+
iterator __position = begin() + (__cposition - cbegin());
485+
if (this->__end_ < this->__cap_) {
486+
if (__position == end()) {
487+
allocator_traits<_Allocator>::construct(
488+
this->__alloc_, std::__to_address(__position), std::forward<_Args>(__args)...);
489+
++this->__end_;
490+
} else {
491+
// Construct a temporary value on the stack, so that in case this throws we haven't modified
492+
// the vector yet. This also takes care of the corner case where we'd be trying to insert
493+
// from an element located in the vector itself, in which case we'd otherwise have to be
494+
// careful about reference invalidation if we didn't make a temporary value.
495+
__temporary_emplace_value<value_type> __tmp(this->__alloc_, std::forward<_Args>(__args)...);
496+
497+
// If the elements are nothrow relocatable, we can relocate them without risking an exception.
498+
// So open up a gap inside the vector, relocate everything to the right and insert the new
499+
// element into the right spot.
500+
//
501+
// Otherwise, we have no choice but to shift everything to the right using move-assignments
502+
// and to move-assign the new element into its final location, to ensure that everything gets
503+
// properly destroyed in case of an exception.
504+
//
505+
// Note that we also require __is_replaceable here for backwards compatibility, because we used
506+
// to perform move-assignments unconditionally. If we didn't enforce that, we would no longer call
507+
// the assignment operator of types that have a funky operator= and expect it to be called in
508+
// vector::insert.
509+
if constexpr (__is_replaceable<value_type>::value && __is_nothrow_allocator_relocatable<_Allocator, value_type>::value) {
510+
// Relocate all the elements in the vector to open up a gap.
511+
std::__uninitialized_allocator_relocate(this->__alloc_, __position, end(), __position + 1);
512+
513+
// Finally, relocate the temporary value into its final location. We don't need to destroy
514+
// the temporary value since it has been relocated.
515+
std::__allocator_relocate_at(this->__alloc_, __tmp.__address(), std::__to_address(__position));
516+
++this->__end_;
517+
} else {
518+
// Ensure the destruction of the temporary no matter what happens exception-wise.
519+
auto __guard = std::__make_scope_guard([&] {
520+
allocator_traits<_Allocator>::destroy(this->__alloc_, __tmp.__address());
521+
});
522+
523+
// Move-construct the (new) last element. There is no object at this location, so
524+
// we must use construction.
525+
allocator_traits<_Allocator>::construct(this->__alloc_, std::__to_address(end()), std::move(*(end() - 1)));
526+
++this->__end_;
527+
528+
// We now have a moved-from object at `end() - 1`. Shift the rest of the range to the right,
529+
// opening up a gap containing a moved-from object at the insert position.
530+
std::move_backward(__position, end() - 1, end());
531+
532+
// Finally, move-assign the new element into its insert position.
533+
*__position = std::move(*__tmp.__address());
534+
}
535+
}
536+
__annotate_increase(1);
537+
return __position;
538+
} else {
539+
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), __position - begin(), this->__alloc_);
540+
__v.emplace_back(std::forward<_Args>(__args)...);
541+
pointer __p = this->__begin_ + (__position - begin());
542+
__p = __swap_out_circular_buffer(__v, __p);
543+
return __make_iter(__p);
544+
}
545+
}
480546

481547
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
482548
insert(const_iterator __position, size_type __n, const_reference __x);
@@ -1163,68 +1229,6 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
11631229
std::move_backward(__from_s, __from_s + __n, __old_last);
11641230
}
11651231

1166-
template <class _Tp, class _Allocator>
1167-
_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
1168-
vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) {
1169-
pointer __p = this->__begin_ + (__position - begin());
1170-
if (this->__end_ < this->__cap_) {
1171-
if (__p == this->__end_) {
1172-
__construct_one_at_end(__x);
1173-
} else {
1174-
__move_range(__p, this->__end_, __p + 1);
1175-
const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
1176-
if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end_), std::addressof(__x)))
1177-
++__xr;
1178-
*__p = *__xr;
1179-
}
1180-
} else {
1181-
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), __p - this->__begin_, this->__alloc_);
1182-
__v.emplace_back(__x);
1183-
__p = __swap_out_circular_buffer(__v, __p);
1184-
}
1185-
return __make_iter(__p);
1186-
}
1187-
1188-
template <class _Tp, class _Allocator>
1189-
_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
1190-
vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) {
1191-
pointer __p = this->__begin_ + (__position - begin());
1192-
if (this->__end_ < this->__cap_) {
1193-
if (__p == this->__end_) {
1194-
__construct_one_at_end(std::move(__x));
1195-
} else {
1196-
__move_range(__p, this->__end_, __p + 1);
1197-
*__p = std::move(__x);
1198-
}
1199-
} else {
1200-
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), __p - this->__begin_, this->__alloc_);
1201-
__v.emplace_back(std::move(__x));
1202-
__p = __swap_out_circular_buffer(__v, __p);
1203-
}
1204-
return __make_iter(__p);
1205-
}
1206-
1207-
template <class _Tp, class _Allocator>
1208-
template <class... _Args>
1209-
_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
1210-
vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
1211-
pointer __p = this->__begin_ + (__position - begin());
1212-
if (this->__end_ < this->__cap_) {
1213-
if (__p == this->__end_) {
1214-
__construct_one_at_end(std::forward<_Args>(__args)...);
1215-
} else {
1216-
__temp_value<value_type, _Allocator> __tmp(this->__alloc_, std::forward<_Args>(__args)...);
1217-
__move_range(__p, this->__end_, __p + 1);
1218-
*__p = std::move(__tmp.get());
1219-
}
1220-
} else {
1221-
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), __p - this->__begin_, this->__alloc_);
1222-
__v.emplace_back(std::forward<_Args>(__args)...);
1223-
__p = __swap_out_circular_buffer(__v, __p);
1224-
}
1225-
return __make_iter(__p);
1226-
}
1227-
12281232
template <class _Tp, class _Allocator>
12291233
_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
12301234
vector<_Tp, _Allocator>::insert(const_iterator __position, size_type __n, const_reference __x) {

0 commit comments

Comments
 (0)