Непосредственная инициализация не копируемых, неподвижных элементов без использования агрегированной инициализации - PullRequest
4 голосов
/ 30 января 2020

Фон

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

template <class T, std::size_t... Dims>
struct multi_array {
    static constexpr std::size_t size() noexcept
    {
        return (Dims * ... * std::size_t{1});
    }
    std::array<T, size()> _elems;
};

Элемент _elems сделан publi c чтобы включить агрегатную инициализацию для не копируемых, неподвижных типов: (предположим, что non_movable имеет явный конструктор (int))

multi_array<non_movable, 2, 3> arr {
    non_movable(0), non_movable(1), non_movable(2),
    non_movable(3), non_movable(4), non_movable(5)
};

Это компилируется благодаря C ++ 17 гарантированному исключению копирования - соответствующие элементы _elems непосредственно инициализируются из нематериализованных значений pr, не требуя конструкторов перемещения.

Задача

Теперь проблема заключается в том, что в приведенном выше объявлении многомерный массив инициализируется как одномерный массив. Я буду называть это «плоской инициализацией», в отличие от «вложенной инициализации»:

multi_array<non_movable, 2, 3> arr {
    { non_movable(0), non_movable(1), non_movable(2) },
    { non_movable(3), non_movable(4), non_movable(5) }
}; // error: too many initializers for 'multi_array<non_movable, 3, 2>'

Как мы можем включить вложенную инициализацию без необходимости изменения базового контейнера, используемого для реализации multi_array из одномерного массива в многомерный массив?

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

Пример минимального воспроизведения

#include <array>
#include <cstddef>

struct non_movable {
    explicit non_movable(int) {}
    non_movable(const non_movable&) = delete;
    non_movable(non_movable&&) = delete;
    non_movable& operator=(const non_movable&) = delete;
    non_movable& operator=(non_movable&&) = delete;
    ~non_movable() = default;
};

template <class T, std::size_t... Dims>
struct multi_array {
    static constexpr std::size_t size() noexcept
    {
        return (Dims * ... * std::size_t{1});
    }
    std::array<T, size()> _elems;
};

int main()
{
    multi_array<non_movable, 3, 2> arr {
        non_movable(0), non_movable(1), non_movable(2),
        non_movable(3), non_movable(4), non_movable(5)
    };
    // multi_array<non_movable, 3, 2> arr {
    //     { non_movable(0), non_movable(1), non_movable(2) },
    //     { non_movable(3), non_movable(4), non_movable(5) }
    // };
    (void)arr;
}

( live демо )

1 Ответ

3 голосов
/ 30 января 2020

Я понятия не имею, как "прозрачно" пропустить нематериализованные значения через конструкторы.

То, что вы хотите, обычно невозможно. Как только значение prvalue передается в качестве аргумента функции, оно либо инициализирует параметр объекта, либо манифестирует временный объект, связанный с опорным параметром. В любом случае, он перестает быть prvalue.

Лучшее, что вы можете сделать для своего конкретного c сценария использования, это сохранить его как совокупность и либо просто принять, что вы должны инициализировать его как "плоский" многомерный массив, или сделать его истинным многомерным массивом, храня массив в массиве. Оба варианта имеют компромиссы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...