@@ -58,6 +58,10 @@ template <typename T, size_t Count>
5858struct uninitialized_storage {
5959 alignas (T) char storage[sizeof (T) * Count];
6060
61+ uninitialized_storage () {
62+ fill (std::begin (storage), std::end (storage), fillChar);
63+ }
64+
6165 T* begin () {
6266 return &reinterpret_cast <T&>(storage);
6367 }
@@ -196,6 +200,122 @@ void test_destroy_n() {
196200 assert (g_alive == 0 );
197201}
198202
203+ struct copy_elision_dest ;
204+
205+ class pinned {
206+ public:
207+ explicit pinned (int n) : n_{n} {}
208+
209+ pinned (const pinned&) = delete ;
210+ pinned& operator =(const pinned&) = delete ;
211+
212+ private:
213+ friend copy_elision_dest;
214+
215+ int n_;
216+ };
217+
218+ class pinned_ioterator {
219+ private:
220+ struct arrow_proxy {
221+ pinned val_;
222+
223+ pinned* operator ->() {
224+ return &val_;
225+ }
226+ };
227+
228+ public:
229+ using iterator_category = input_iterator_tag;
230+ using difference_type = int ;
231+ using value_type = pinned;
232+ using pointer = arrow_proxy;
233+ using reference = pinned;
234+
235+ explicit pinned_ioterator (int n) : n_{n} {}
236+
237+ pinned operator *() const {
238+ return pinned{n_};
239+ }
240+ pinned_ioterator& operator ++() {
241+ ++n_;
242+ return *this ;
243+ }
244+ pinned_ioterator operator ++(int ) {
245+ auto old = *this ;
246+ ++*this ;
247+ return old;
248+ }
249+
250+ arrow_proxy operator ->() const {
251+ return arrow_proxy{pinned{n_}};
252+ }
253+
254+ friend bool operator ==(pinned_ioterator i, pinned_ioterator j) {
255+ return i.n_ == j.n_ ;
256+ }
257+ #if !_HAS_CXX20
258+ friend bool operator !=(pinned_ioterator i, pinned_ioterator j) {
259+ return !(i == j);
260+ }
261+ #endif // !_HAS_CXX20
262+
263+ private:
264+ int n_;
265+ };
266+
267+ struct copy_elision_dest {
268+ explicit copy_elision_dest (pinned x) : n_{x.n_ } {}
269+
270+ int n_;
271+ };
272+
273+ // std::uninitialized_copy/_n are required to perform guaranteed copy elision since C++17.
274+ void test_guaranteed_copy_elision_uninitialized_copy () {
275+ constexpr int len = 42 ;
276+
277+ uninitialized_storage<copy_elision_dest, len> us;
278+ uninitialized_copy (pinned_ioterator{0 }, pinned_ioterator{len}, us.begin ());
279+ for (int i = 0 ; i != len; ++i) {
280+ assert (us.begin ()[i].n_ == i);
281+ }
282+ destroy (us.begin (), us.end ());
283+ }
284+
285+ void test_guaranteed_copy_elision_uninitialized_copy_n () {
286+ constexpr int len = 42 ;
287+
288+ uninitialized_storage<copy_elision_dest, len> us;
289+ uninitialized_copy_n (pinned_ioterator{0 }, len, us.begin ());
290+ for (int i = 0 ; i != len; ++i) {
291+ assert (us.begin ()[i].n_ == i);
292+ }
293+ destroy (us.begin (), us.end ());
294+ }
295+
296+ // Also test LWG-3918 "std::uninitialized_move/_n and guaranteed copy elision".
297+ void test_guaranteed_copy_elision_uninitialized_move () {
298+ constexpr int len = 42 ;
299+
300+ uninitialized_storage<copy_elision_dest, len> us;
301+ uninitialized_move (pinned_ioterator{0 }, pinned_ioterator{len}, us.begin ());
302+ for (int i = 0 ; i != len; ++i) {
303+ assert (us.begin ()[i].n_ == i);
304+ }
305+ destroy (us.begin (), us.end ());
306+ }
307+
308+ void test_guaranteed_copy_elision_uninitialized_move_n () {
309+ constexpr int len = 42 ;
310+
311+ uninitialized_storage<copy_elision_dest, len> us;
312+ uninitialized_move_n (pinned_ioterator{0 }, len, us.begin ());
313+ for (int i = 0 ; i != len; ++i) {
314+ assert (us.begin ()[i].n_ == i);
315+ }
316+ destroy (us.begin (), us.end ());
317+ }
318+
199319int main () {
200320 test_uninitialized_move ();
201321 test_uninitialized_move_n ();
@@ -206,4 +326,9 @@ int main() {
206326 test_destroy_at ();
207327 test_destroy ();
208328 test_destroy_n ();
329+
330+ test_guaranteed_copy_elision_uninitialized_copy ();
331+ test_guaranteed_copy_elision_uninitialized_copy_n ();
332+ test_guaranteed_copy_elision_uninitialized_move ();
333+ test_guaranteed_copy_elision_uninitialized_move_n ();
209334}
0 commit comments