Запрет инициализации статического массива недостаточным количеством аргументов

Всё ещё мучаю свой учебный многомерная матрица. Всё ещё инициализация. Первым делом при инициализации массива я инициализирую поле хранящее структуру матрицы - одномерный массив его размеров. Длина этого массива равна размерности матрицы. Элементы массива - количество элементов матрицы по каждой из размерности. Так вот, один из конструкторов массива имеем переменное число аргументов (parameter pack) - просто перечисление размеров матрицы.

   using dimension_t = unsigned char;

   template <dimension_t Dimension_, std::unsigned_integral SizeType_ = size_t>
   class TMultiDimParam {
   private:

       SizeType_ data[Dimension_];

   public:

       template <std::integral ...size_types>
       TMultiDimParam(const size_types... sizes) :
           data{ static_cast<SizeType_>(sizes)... }
       {}

   };

Задача: Нужно на этапе компиляции запретить вызов конструктора с неправильным числом аргументов.

К счастью, чтобы запретить превышение числа аргументов ничего делать не нужно. Так как при попытке

sib::TMultiDimParam<3> mds0(1, 2, 3, 4);

в строке

data{ static_cast<SizeType_>(sizes)... }

тут же возникнет ошибка

C2078 слишком много инициализаторов

Но вот если я вызовы

sib::TMultiDimParam<3> mds0(1, 2);

То это скомпилируется и массив дозаполнится нулями. Так вот этого я и хочу избежать.

Решение заключается в том, чтобы обернуть тип элементов массива (SizeType_) в класс-обёртку без конструктора по умолчанию.

   using dimension_t = unsigned char;

   template <dimension_t Dimension_, std::unsigned_integral SizeType_ = size_t>
   class TMultiDimParam {
   private:

       template<std::unsigned_integral T>
       struct TDimParam {
           T data;
           TDimParam() = delete;
           TDimParam(T value) : data(value) {};
       };

       TDimParam<SizeType_> data[Dimension_];
       //SizeType_ data[Dimension_];

   public:

       template <std::integral ...size_types>
       TMultiDimParam(const size_types... sizes) :
           data{ static_cast<TDimParam<SizeType_>>(sizes)... }
           //data{ static_cast<SizeType_>(sizes)... }
       {}

   };

И, о чудо, оно заработало. Теперь попытка

sib::TMultiDimParam<3> mds0(1, 2);

приводит к ошибке

C2280 "sib::TMultiDimParam<3,size_t>::TDimParam<SizeType_>::TDimParam(void)": предпринята попытка ссылки на удаленную функцию

И тут для меня началась довольно зыбкая почва, так как хотелось бы (я думаю, в этом первый смысл любого контейнера после непосредственного хранения данных в виде какой-то структуры), чтобы подобный финт не влиял на производительность. И вроде как не влияет. Я сравнил дизассемблер в режиме release, и оба варианта оказались идентичны. Даже оптимизации (где он не создаёт временный массив, который я потом суммирую, а сразу суммирует числа) сработали одинаково. Но это всё только "вроде как", ибо для меня, как я уже сказал, это зыбкая почва. Слишком поверхностные знания. Я был бы очень признателен, если бы кто-нибудь из понимающих из спортивного интереса или из жалости к лентяю, который не хочет выучить всё всё, сказал бы: В правильном направлении ли я иду или нет? Или есть способ проще/понятнее/правильнее?


Ответы (2 шт):

Автор решения: HolyBlackCat

Способ проще есть:

template <std::integral ...size_types>
requires (sizeof...(size_types) == Dimension_)
TMultiDimParam(const size_types... sizes)
    : data{ static_cast<SizeType_>(sizes)... }
{}
→ Ссылка
Автор решения: user7860670

Нет никакой необходимости прибегать к концептам или что-то кастовать, в прошлом же вопросе выясняли, как избегать проблем с целочисленными преобразованиями и переполнениями.

template
<
    ::std::size_t x_dimensions
>
class
TMultiDimParam
{
    private: ::std::array<::std::size_t, x_dimensions> m_data;

    public: template
    <
        typename x_SizePack
    >
    explicit
    TMultiDimParam
    (
        x_SizePack const... size_pack
    )
    :   m_data{size_pack...}
    {
        static_assert((::std::is_same_v<x_SizePack, ::std::size_t>::value and ...));
        static_assert(x_dimensions == sizeof...(size_pack));
        return;
    }
};
→ Ссылка