Ответ Квентина очень хорош, и с чем бы я пошел.
Единственным недостатком является «бесполезная» генерация последовательности индексов [0, 1, 2, ...], значения которой мы игнорируем и заменяем нашими собственными.
Если мы хотим напрямую создать повторяющиеся значения, мы можем написать собственный код генератора (который немного более детальный):
Начните с создания типа, который может содержать несколько значений std::size_t
, добавив псевдоним a std::integer_sequence
:
template<std::size_t... vals>
using value_sequence = std::integer_sequence<std::size_t, vals...>;
Цель состоит в том, чтобы в конечном итоге создать value_sequence<4, 4, 4>
, а затем создать экземпляр Eigen::Sizes
, используя эти 4.
Следующее, что нам нужно сделать, это объединить две последовательности, потому что мы собираемся построить их так:
concat(value_sequence<4>, value_sequence<4>) --> value_sequence<4, 4>
Мы можем сделать это с помощью метода-заглушки, который принимает два типа value_sequence
и возвращает объединенный результат. Обратите внимание, что мы никогда не пишем определения для этого метода; мы просто используем преимущества системы типов для написания меньшего количества кода, чем требовалось бы для специализации шаблона:
template<std::size_t... lhs, std::size_t... rhs>
constexpr auto concat(value_sequence<lhs...>, value_sequence<rhs...>) -> value_sequence<lhs..., rhs...>;
На данный момент у нас достаточно машин для создания value_sequence<4,4,4>
, поэтому теперь нам нужен способ указать значение, которое мы хотим использовать (4), и количество раз, чтобы повторить его (3) для его получения:
template<std::size_t value, std::size_t num_repeats>
struct repeated_value
{
using left_sequence = value_sequence<value>;
using right_sequence = typename repeated_value<value, num_repeats-1>::type;
using type = decltype(concat(left_sequence{}, right_sequence{}));
};
repeated_value<4, 3>::type
производит value_sequence<4, 4, 4>
.
Поскольку repeated_value<...>::type
является рекурсивным, нам необходимо предоставить базовый вариант с частичной специализацией:
template<std::size_t value>
struct repeated_value<value, 1>
{
using type = value_sequence<value>;
};
Отлично. Нам осталось только получить класс Eigen::Sizes
и тип value_sequence<4, 4, 4>
и произвести Eigen::Sizes<4, 4, 4>
.
Мы можем сделать это снова с частичной специализацией шаблона:
template<template<std::size_t...> class T, class...>
struct InstantiateWithRepeatedVals;
template<template<std::size_t...> class T, std::size_t... vals>
struct InstantiateWithRepeatedVals<T, value_sequence<vals...>>
{
using type = T<vals...>;
};
Вот оно! Добавьте несколько помощников, чтобы упростить его использование, и все готово:
template<std::size_t value, std::size_t num_repeats>
using repeated_value_t = typename repeated_value<value, num_repeats>::type;
template<template<std::size_t...> class T, std::size_t Value, std::size_t N>
using InstantiateWithRepeatedVals_t = typename InstantiateWithRepeatedVals<T, repeated_value_t<Value, N>>::type;
Теперь мы можем использовать его так:
using my_type = InstantiateWithRepeatedVals_t<EigenSizes, 4, 3>;
static_assert(std::is_same_v<my_type, EigenSizes<4, 4, 4>>);