Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moving to uninitialized memory, or how raw_storage_iterator works

Tags:

c++

c++11

I want to move a range of objects into uninitialized memory (using move-construction). Since there is no move-counterpart to std::uninitialized_copy, I came up with two options: either use std::move with raw_storage_iterator, or resort to the manual loop:

T* dest = get_memory();
// option one
std::move(first, last, std::raw_storage_iterator<T*, T>(dest));
// option two
for (auto i=first; i != last; ++i, ++dest)
{
    new(dest) T(std::move(*i));
}

Will the first option do the move-construction (thus being equivalent to the second), or copy construction, or default construction followed by move assignment? Are there other considerations to prefer one option or another?

like image 509
Ilya Popov Avatar asked Jul 18 '15 21:07

Ilya Popov


1 Answers

std::raw_storage_iterator uses a placement-new operator on assignment which in c++11 defined in terms of a copy-assignment operator taking a const lvalue reference:

§ 20.7.10 [storage.iterator]/p1:

raw_storage_iterator& operator=(const T& element);

which always calls a copy constructor (even when invoked with an rvalue).

This case has been reported as LWG issue 2127 which adds support for move-constructible types by introducing another assignment operator taking an rvalue reference, meaning it will be possible to move-construct elements to an uninitialized memory once the proposed change is adopted. Until that update takes place, you need to rely on your own for-loop. Alternatively, you could leverage the std::uninitialized_copy algorithm defined as follows:

§ 20.7.12.2 [uninitialized.copy]/p1:

for (; first != last; ++result, ++first)
    ::new (static_cast<void*>(&*result))
        typename iterator_traits<ForwardIterator>::value_type(*first);

Once you wrap your input iterators using the std::make_move_iterator helper function, you'll get the same effect as your hand-written for-loop:

std::uninitialized_copy(std::make_move_iterator(first)
                      , std::make_move_iterator(last)
                      , dest);

c++17 extends the set of algorithms by introducing std::uninitialized_move:

std::uninitialized_move(first, last, dst);
like image 197
Piotr Skotnicki Avatar answered Nov 12 '22 05:11

Piotr Skotnicki