Skip to content

Commit 7d6d354

Browse files
committed
Formulate vector::emplace and insert in terms of relocation
1 parent 79e6086 commit 7d6d354

File tree

2 files changed

+84
-65
lines changed

2 files changed

+84
-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: 66 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -472,11 +472,74 @@ 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.
493+
__temporary_emplace_value<value_type> __tmp(this->__alloc_, std::forward<_Args>(__args)...);
494+
auto __guard = std::__make_exception_guard([&] {
495+
allocator_traits<_Allocator>::destroy(this->__alloc_, __tmp.__address());
496+
});
497+
498+
// If the elements are nothrow relocatable, we can relocate them without risking an exception.
499+
// So open up a gap inside the vector, relocate everything to the right and insert the new
500+
// element into the right spot.
501+
//
502+
// Otherwise, we have no choice but to shift everything to the right using move-assignments
503+
// and to move-assign the new element into its final location, to ensure that everything gets
504+
// properly destroyed in case of an exception.
505+
//
506+
// Note that we also require __is_replaceable here for backwards compatibility, because we used
507+
// to perform move-assignments unconditionally. If we didn't enforce that, we would no longer call
508+
// the assignment operator of types that have a funky operator= and expect it to be called in
509+
// vector::insert.
510+
if constexpr (__is_replaceable<value_type>::value && __is_nothrow_allocator_relocatable<_Allocator, value_type>::value) {
511+
// Relocate all the elements in the vector to open up a gap.
512+
std::__uninitialized_allocator_relocate(this->__alloc_, __position, end(), __position + 1);
513+
514+
// Finally, relocate the temporary value into its final location.
515+
std::__allocator_relocate_at(this->__alloc_, __tmp.__address(), std::__to_address(__position));
516+
++this->__end_;
517+
__guard.__complete();
518+
} else {
519+
// First, move-construct the (new) last element. There is no object at this location, so
520+
// we must use construction.
521+
allocator_traits<_Allocator>::construct(this->__alloc_, std::__to_address(end()), std::move(*(end() - 1)));
522+
++this->__end_;
523+
524+
// We now have a moved-from object at `end() - 1`. Shift the rest of the range to the right,
525+
// opening up a gap containing a moved-from object at the insert position.
526+
std::move_backward(__position, end() - 1, end());
527+
528+
// Finally, move-assign the new element into its insert position.
529+
*__position = std::move(*__tmp.__address());
530+
__guard.__complete();
531+
}
532+
}
533+
__annotate_increase(1);
534+
return __position;
535+
} else {
536+
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), __position - begin(), this->__alloc_);
537+
__v.emplace_back(std::forward<_Args>(__args)...);
538+
pointer __p = this->__begin_ + (__position - begin());
539+
__p = __swap_out_circular_buffer(__v, __p);
540+
return __make_iter(__p);
541+
}
542+
}
480543

481544
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
482545
insert(const_iterator __position, size_type __n, const_reference __x);
@@ -1163,68 +1226,6 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
11631226
std::move_backward(__from_s, __from_s + __n, __old_last);
11641227
}
11651228

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-
12281229
template <class _Tp, class _Allocator>
12291230
_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
12301231
vector<_Tp, _Allocator>::insert(const_iterator __position, size_type __n, const_reference __x) {

0 commit comments

Comments
 (0)