Skip to content

Commit 8131df8

Browse files
Implement LWG-3918
1 parent 5e0ddad commit 8131df8

File tree

4 files changed

+151
-9
lines changed

4 files changed

+151
-9
lines changed

stl/inc/execution

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,6 @@ template <>
137137
struct is_execution_policy<execution::unsequenced_policy> : true_type {};
138138
#endif // _HAS_CXX20
139139

140-
template <class _Ty, class _FwdIt>
141-
void _Construct_in_place_by_deref(_Ty& _Val, const _FwdIt& _Iter) {
142-
::new (static_cast<void*>(_STD addressof(_Val))) _Ty(*_Iter);
143-
}
144-
145140
template <class _Ty, class _UnaryOp, class _FwdIt>
146141
void _Construct_in_place_by_transform_deref(_Ty& _Val, _UnaryOp _Transform_op, const _FwdIt& _Iter) {
147142
::new (static_cast<void*>(_STD addressof(_Val))) _Ty(_Transform_op(*_Iter));

stl/inc/memory

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ _NoThrowFwdIt uninitialized_copy_n(const _InIt _First, const _Diff _Count_raw, _
141141
_Uninitialized_backout<decltype(_UDest)> _Backout{_UDest};
142142

143143
for (; _Count > 0; --_Count, (void) ++_UFirst) {
144-
_Backout._Emplace_back(*_UFirst);
144+
_Backout._Emplace_back_deref(_UFirst);
145145
}
146146

147147
_UDest = _Backout._Release();
@@ -294,7 +294,7 @@ pair<_InIt, _NoThrowFwdIt> uninitialized_move_n(_InIt _First, const _Diff _Count
294294
_Uninitialized_backout<decltype(_UDest)> _Backout{_UDest};
295295

296296
for (; _Count > 0; --_Count, (void) ++_UFirst) {
297-
_Backout._Emplace_back(_STD move(*_UFirst));
297+
_Backout._Emplace_back_deref_move(_UFirst);
298298
}
299299

300300
_UDest = _Backout._Release();

stl/inc/xmemory

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,11 @@ void _Return_temporary_buffer(_Ty* const _Pbuf) noexcept {
16011601
}
16021602
}
16031603

1604+
template <class _Ty, class _InIt>
1605+
void _Construct_in_place_by_deref(_Ty& _Val, const _InIt& _Iter) {
1606+
::new (static_cast<void*>(_STD addressof(_Val))) _Ty(*_Iter);
1607+
}
1608+
16041609
template <class _NoThrowFwdIt>
16051610
struct _NODISCARD _Uninitialized_backout {
16061611
// struct to undo partially constructed ranges in _Uninitialized_xxx algorithms
@@ -1625,6 +1630,29 @@ struct _NODISCARD _Uninitialized_backout {
16251630
++_Last;
16261631
}
16271632

1633+
template <class _InIt>
1634+
_CONSTEXPR20 void _Emplace_back_deref(const _InIt& _Iter) {
1635+
// construct a new element at *_Last from the result of dereferencing _Iter and increment.
1636+
if constexpr (is_reference_v<decltype(*_Iter)>) {
1637+
_STD _Construct_in_place(*_Last, *_Iter);
1638+
} else {
1639+
_STD _Construct_in_place_by_deref(*_Last, _Iter);
1640+
}
1641+
++_Last;
1642+
}
1643+
1644+
template <class _InIt>
1645+
_CONSTEXPR20 void _Emplace_back_deref_move(const _InIt& _Iter) {
1646+
// construct a new element at *_Last from the result of dereferencing _Iter and increment,
1647+
// with lvalue cast to xvalue if necessary for uninitialized_move(_n).
1648+
if constexpr (is_reference_v<decltype(*_Iter)>) {
1649+
_STD _Construct_in_place(*_Last, _STD move(*_Iter));
1650+
} else {
1651+
_STD _Construct_in_place_by_deref(*_Last, _Iter);
1652+
}
1653+
++_Last;
1654+
}
1655+
16281656
constexpr _NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last
16291657
_First = _Last;
16301658
return _Last;
@@ -1644,7 +1672,7 @@ _CONSTEXPR20 _NoThrowFwdIt _Uninitialized_move_unchecked(_InIt _First, const _In
16441672
}
16451673
_Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest};
16461674
for (; _First != _Last; ++_First) {
1647-
_Backout._Emplace_back(_STD move(*_First));
1675+
_Backout._Emplace_back_deref_move(_First);
16481676
}
16491677

16501678
return _Backout._Release();
@@ -1918,7 +1946,7 @@ _CONSTEXPR20 _NoThrowFwdIt _Uninitialized_copy_unchecked(_InIt _First, const _In
19181946

19191947
_Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest};
19201948
for (; _First != _Last; ++_First) {
1921-
_Backout._Emplace_back(*_First);
1949+
_Backout._Emplace_back_deref(_First);
19221950
}
19231951

19241952
return _Backout._Release();

tests/std/tests/P0040R3_extending_memory_management_tools/test.cpp

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,120 @@ void test_destroy_n() {
196196
assert(g_alive == 0);
197197
}
198198

199+
struct copy_elision_dest;
200+
201+
class pinned {
202+
public:
203+
explicit pinned(int n) : n_{n} {}
204+
205+
pinned(const pinned&) = delete;
206+
pinned& operator=(const pinned&) = delete;
207+
208+
private:
209+
friend copy_elision_dest;
210+
211+
int n_;
212+
};
213+
214+
class pinned_ioterator {
215+
private:
216+
struct arrow_proxy {
217+
pinned val_;
218+
219+
pinned* operator->() {
220+
return &val_;
221+
}
222+
};
223+
224+
public:
225+
using value_type = pinned;
226+
using difference_type = int;
227+
using reference = pinned;
228+
using pointer = arrow_proxy;
229+
using iterator_category = std::input_iterator_tag;
230+
231+
explicit pinned_ioterator(int n) : n_{n} {}
232+
233+
pinned operator*() const {
234+
return pinned{n_};
235+
}
236+
pinned_ioterator& operator++() {
237+
++n_;
238+
return *this;
239+
}
240+
pinned_ioterator operator++(int) {
241+
auto old = *this;
242+
++*this;
243+
return old;
244+
}
245+
246+
arrow_proxy operator->() const {
247+
return arrow_proxy{pinned{n_}};
248+
}
249+
250+
friend bool operator==(pinned_ioterator i, pinned_ioterator j) {
251+
return i.n_ == j.n_;
252+
}
253+
#if !_HAS_CXX20
254+
friend bool operator!=(pinned_ioterator i, pinned_ioterator j) {
255+
return !(i == j);
256+
}
257+
#endif // !_HAS_CXX20
258+
259+
private:
260+
int n_;
261+
};
262+
263+
struct copy_elision_dest {
264+
public:
265+
copy_elision_dest() = default;
266+
explicit copy_elision_dest(pinned x) : n_{x.n_} {}
267+
268+
int n_;
269+
};
270+
271+
// std::uninitialized_copy/_n are required to perform guaranteed copy elision since C++17.
272+
void test_guaranteed_copy_elision_uninitialized_copy() {
273+
constexpr int len = 42;
274+
275+
copy_elision_dest d[len];
276+
uninitialized_copy(pinned_ioterator{0}, pinned_ioterator{len}, d);
277+
for (int i = 0; i != len; ++i) {
278+
assert(d[i].n_ == i);
279+
}
280+
}
281+
282+
void test_guaranteed_copy_elision_uninitialized_copy_n() {
283+
constexpr int len = 42;
284+
285+
copy_elision_dest d[len];
286+
uninitialized_copy_n(pinned_ioterator{0}, len, d);
287+
for (int i = 0; i != len; ++i) {
288+
assert(d[i].n_ == i);
289+
}
290+
}
291+
292+
// Also test LWG-3918 "std::uninitialized_move/_n and guaranteed copy elision".
293+
void test_guaranteed_copy_elision_uninitialized_move() {
294+
constexpr int len = 42;
295+
296+
copy_elision_dest d[len];
297+
uninitialized_move(pinned_ioterator{0}, pinned_ioterator{len}, d);
298+
for (int i = 0; i != len; ++i) {
299+
assert(d[i].n_ == i);
300+
}
301+
}
302+
303+
void test_guaranteed_copy_elision_uninitialized_move_n() {
304+
constexpr int len = 42;
305+
306+
copy_elision_dest d[len];
307+
uninitialized_move_n(pinned_ioterator{0}, len, d);
308+
for (int i = 0; i != len; ++i) {
309+
assert(d[i].n_ == i);
310+
}
311+
}
312+
199313
int main() {
200314
test_uninitialized_move();
201315
test_uninitialized_move_n();
@@ -206,4 +320,9 @@ int main() {
206320
test_destroy_at();
207321
test_destroy();
208322
test_destroy_n();
323+
324+
test_guaranteed_copy_elision_uninitialized_copy();
325+
test_guaranteed_copy_elision_uninitialized_copy_n();
326+
test_guaranteed_copy_elision_uninitialized_move();
327+
test_guaranteed_copy_elision_uninitialized_move_n();
209328
}

0 commit comments

Comments
 (0)