Инициализация Brace массива-члена с другим массивом constexpr

56
3

Рассмотрим следующий примерный пример кода:

using matrix_t = double[2][2];

constexpr matrix_t zero_matrix = {
{0.0, 0.0},
{0.0, 0.0}
};

constexpr matrix_t identity_matrix = {
{1.0, 0.0},
{0.0, 1.0}
};

struct wrapped_matrix_t
{
matrix_t matrix;
};

constexpr wrapped_matrix_t zero_wrapped_matrix = {
//zero_matrix
{
{zero_matrix[0][0], zero_matrix[0][1]},
{zero_matrix[1][0], zero_matrix[1][1]}
}
};

constexpr wrapped_matrix_t identity_wrapped_matrix = {
//identity_matrix
{
{identity_matrix[0][0], identity_matrix[0][1]},
{identity_matrix[1][0], identity_matrix[1][1]}
}
};

Теперь я хотел бы иметь возможность использовать в constexpr массивы zero_matrix и identity_matrix для инициализации членов matrix_t в других типах. Однако это кажется невозможным. Или, по крайней мере, простое использование имени не работает.

Лучшее, что я получаю, это повторное использование значений, относящихся к индексам. Но это далеко не идеально.

Есть ли способ использовать zero_matrix и identity_matrix непосредственно в таких инициализациях?

(Я проверяю GCC 6.3.0 с -Wall -Wextra -pedantic -std=c++11)

спросил(а) 2017-05-12T12:09:00+03:00 3 года, 4 месяца назад
1
Решение
57

Если вы не можете использовать std::array и не имеете доступа к реализации wrapped_matrix_t, вы можете использовать метапрограммирование для генерации индексации исходного массива при инициализации ваших оберток. Окончательный код будет выглядеть так:

constexpr wrapped_matrix_t zero_wrapped_matrix = from_array(zero_matrix);
constexpr wrapped_matrix_t identity_wrapped_matrix = from_array(identity_matrix);

Я предполагаю, что matrix_t является double[2 * 2], но нижеприведенная техника может быть обобщена на массивы N -dimension.

Вот подробности реализации:

template <typename Array, std::size_t... Is>
constexpr auto from_array_impl(const Array& arr, std::index_sequence<Is...>)
{
return wrapped_matrix_t{arr[Is]...};
}

template <std::size_t N>
constexpr auto from_array(const double(&arr)[N])
{
return from_array_impl(arr, std::make_index_sequence<N>());
}

Я в основном сопоставляю размер массива со ссылкой и 0..N из него индексную последовательность 0..N. Затем я создаю wrapped_matrix_t путем индексирования массива с помощью последовательности.

живой пример на wandbox

Здесь возможная реализация для 2D-массивов:

template <std::size_t W, typename Array, std::size_t... Is>
constexpr auto from_array_impl(const Array& arr, std::index_sequence<Is...>)
{
return wrapped_matrix_t{(arr[Is % W][Is / W])...};
}

template <std::size_t W, std::size_t H>
constexpr auto from_array(const double(&arr)[W][H])
{
return from_array_impl<W>(arr, std::make_index_sequence<W * H>());
}

живой пример на wandbox

ответил(а) 2017-05-12T12:44:00+03:00 3 года, 4 месяца назад
56

Вы можете использовать std::array и изменить свое представление на 1D-массив.

using matrix_t = std::array<double, 2 * 2>;

constexpr matrix_t zero_matrix = {
{0.0, 0.0,
0.0, 0.0}
};

constexpr matrix_t identity_matrix = {
{1.0, 0.0,
0.0, 1.0}
};

struct wrapped_matrix_t
{
matrix_t matrix;
};

constexpr wrapped_matrix_t zero_wrapped_matrix{zero_matrix};
constexpr wrapped_matrix_t identity_wrapped_matrix{zero_matrix};

живой пример на wandbox


Чтобы получить доступ к массиву 1D, как если бы он был двумерным, вы можете использовать следующую формулу: (x, y) => x + y * width.

Можно создать обертку вокруг std::array которая обеспечивает 2D-подобный интерфейс, если вы этого желаете.

ответил(а) 2017-05-12T12:18:00+03:00 3 года, 4 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема