Вариадическое пользовательское преобразование / конструктор



Учитывая следующий векторный класс (предназначенный для 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;
}
562   3  

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

    Ничего не найдено.