C ++ правильный способ перемещения элемента массива align_storage - PullRequest
2 голосов
/ 10 июня 2019

Итак, как говорится в заголовке, мне интересно, как правильно перемещать элемент в массиве, например:

std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> data;

Это так же просто, как делать:

data[dst] = data[src];

Или мне нужно добавить что-то еще, например, перемещение, поскольку его хранилище не инициализировано, нужно ли мне использовать конструкторы копирования или перемещения, например:

new (&data[dst]) T(std::move(data[src]));

Поскольку данные [src]неправильный тип T, мне нужно вместо этого сделать:

new (&data[dst]) T(std::move(*std::launder(reinterpret_cast<T*>(&data[src])));

Я ищу наиболее гибкий способ перемещения элемента для всего, что может быть T, включая перемещение только типов и т. д.

По сути, я создаю упакованный массив, который всегда перемещает элементы, чтобы они были смежными в памяти, даже если они удалены, чтобы предотвратить дыры в активной части массива.

Редактировать: По желанию комментариевминимальный пример, я думаю, что-то вроде:

template<class T, std::size_t N>
class SimpleExampleClass {
    std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> data;

public:
    void move_element(std::size_t src, std::size_t dst) {
        // data[dst] = data[src]; ?
        // or
        // new (&data[dst]) T(std::move(data[src]));
        // or
        // new (&data[dst]) T(std::move(*std::launder(reinterpret_cast<T*>(&data[src])));
        // or
        // something else?

        // then I would need some way to clean up the src element, not sure what would suffice for that.
        // as calling a destructor on it could break something that was moved potentially?
    }

    // Other functions to manipulate the data here... (example below)
    template<typename ...Args>
    void emplace_push(Args&&... args) noexcept {
        new (&data[/*some index*/]) T(std::forward<Args>(args)...);
    }

    void push(T item) noexcept {
        emplace_push(std::move(item));
    }
};

1 Ответ

3 голосов
/ 10 июня 2019

std::aligned_storage сам по себе, грубо говоря, просто набор байтов. Нечего двигаться, и std::move(data[src]) - просто запрет. Сначала вы должны использовать размещение new для создания объекта, а затем вы можете переместить этот объект, создав его в новом месте.

Простой пример:

auto ptr = new (&data[0]) T();
new (&data[1]) T(std::move(*ptr));
std::destroy_at(ptr);

в случае T, что-то вроде unique_ptr, или в любом другом подобном краевом случае, не должно быть никаких проблем с правильным вызовом destroy для индекса старого элемента?

Движение от объекта оставляет его в каком-то действительном состоянии, и объект все еще должен быть уничтожен.

, поскольку data[0] - это просто набор байтов, сработает ли указатель на него, или этот указатель необходимо переинтерпретировать приведение перед использованием в конструкторе перемещения?

Это будет работать, если оно украшено reinterpret_cast и std::launder, как вы написали в своем вопросе:

new (&data[1]) T(std::move(*std::launder(reinterpret_cast<T*>(&data[0]))));

Стандартная библиотека содержит несколько полезных функций для работы с неинициализированной памятью. Полный список можно найти здесь (см. Раздел Неинициализированное хранилище ).

...