Запрет инициализации статического массива недостаточным количеством аргументов
Всё ещё мучаю свой учебный многомерная матрица. Всё ещё инициализация. Первым делом при инициализации массива я инициализирую поле хранящее структуру матрицы - одномерный массив его размеров. Длина этого массива равна размерности матрицы. Элементы массива - количество элементов матрицы по каждой из размерности. Так вот, один из конструкторов массива имеем переменное число аргументов (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 шт):
Способ проще есть:
template <std::integral ...size_types>
requires (sizeof...(size_types) == Dimension_)
TMultiDimParam(const size_types... sizes)
: data{ static_cast<SizeType_>(sizes)... }
{}
Нет никакой необходимости прибегать к концептам или что-то кастовать, в прошлом же вопросе выясняли, как избегать проблем с целочисленными преобразованиями и переполнениями.
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;
}
};