Вариадическое пользовательское преобразование / конструктор
Учитывая следующий векторный класс (предназначенный для 2d, 3d или 4d векторной математики), который использует std::array, можно ли определить вариадический конструктор и/или конструктор преобразования?
В качестве последующего вопроса, Является ли это плохой практикой? Я нахожу, что мне нужно конвертировать в-из целого числа, float и double довольно много.
Я знаю, что неявные преобразования будут происходить, но мой компилятор будет предупреждать меня об этом (я не хочу отключать их вообще).
#include <array>
template<typename T0, size_t S>
class Vec
{
public:
static_assert(S > 1 && S < 5, "vector dimension must be between 2 and 4");
static_assert(std::is_arithmetic<T0>::value, "type must be arithmetic");
std::array<T0, S> v;
Vec() = default;
template<class T1, class T2>
Vec(T1 t1, T2 t2) : v({ static_cast<T0>(t1),
static_cast<T0>(t2) }) {}
template<class T1, class T2, class T3>
Vec(T1 t1, T2 t2, T3 t3) : v({ static_cast<T0>(t1),
static_cast<T0>(t2),
static_cast<T0>(t3) }) {}
template<class T1, class T2, class T3, class T4>
Vec(T1 t1, T2 t2, T3 t3, T4 t4) : v({ static_cast<T0>(t1),
static_cast<T0>(t2),
static_cast<T0>(t3),
static_cast<T0>(t4) }) {}
};
int main(void)
{
auto foo1 = Vec<float, 2>(1, 2);
auto foo2 = Vec<float, 2>(1.0f, 2.0f);
auto foo3 = Vec<float, 2>(1.0, 2.0);
auto foo4 = Vec<float, 2>(1u, 2u);
return 0;
}
3 ответов:
Конечно, это возможно.
Кстати, в таком случае вам не нужен конструктор по умолчанию, так как этот вариадический конструктор еще лучше в том смысле, что он обнулит Ваш массив...
template<typename T0, size_t S> class Vec { public: static_assert(S > 1 && S < 3, "vector dimension must be between 2 and 4"); static_assert(std::is_arithmetic<T0>::value, "type must be arithmetic"); std::array<T0, S> v; template <typename ...T> Vec(T&& ...a) : v{{ static_cast<T0>(std::forward<T>(a))...}} {} };
Если вы добавляете последовательность индексов в шаблон класса:
template<typename T0, size_t S, typename = std::make_index_sequence<S>> class Vec;Вы можете использовать эту дополнительную вещь для определения обоих конструкторов:
template<typename T0, size_t S, size_t... Idx> class Vec<T0, S, std::index_sequence<Idx...>> { template <size_t> using ith_T = T0; ... Vec(ith_T<Idx>... ts) : v({ts...}) // no cast necessary, they're already T0 { } template <typename U, typename = std::enable_if_t<std::is_convertible<U, T0>::value> > Vec(const Vec<U, S>& rhs) : v({static_cast<T0>(rhs.v[Idx])...}) { } ... };
Из вашего примера мне кажется, что представленные значения имеют один и тот же тип в любом случае. Итак, почему бы вам не использовать
std::initializer_list? Более того, вы приводите их к типуT0, Прежде чем передать их во внутреннийstd::array, так что, возможно, это того стоит. На самом деле вас не волнуют типы аргументов, поскольку вы можете привести их кT0.
Comments