Перенаправление списка инициализаторов / агрегатной инициализации члену std :: array - PullRequest
1 голос
/ 27 мая 2020

У меня есть класс, который инкапсулирует тип контейнера, совместимый с stl, который является единственным членом класса и дает множество векторных математических функций, которые могут быть применены к этому вектору.

Этот класс имеет различные типы конструкторов, один из них - конструктор, который принимает список инициализаторов:

template <class Type, class VectorType = std::vector<Type>>
class MathVector
{
public:
    using initializer_list = std::initializer_list<Type>;

    MathVector (initializer_list il) : vector(il) {}

    // many more constructors and member functions here

private:
    VectorType vector:
}

Хотя что-то вроде MathVector<int> foo = { 1, 2, 3 } работает хорошо, MathVector<int, std::array<int, 3>> bar = { 1, 2, 3 } не компилируется на clang с ошибкой типа

(86, 55) No matching constructor for initialization of 'std::__1::array<int, 3>'

std::vector<int> foo = { 1, 2, 3 } и std::array<int, 3> bar = { 1, 2, 3 } работают, поэтому я предполагаю, что, несмотря на тот же синтаксис, std::array на самом деле не создается через список инициализаторов в этом случае. Это предположение усиливается при просмотре источников библиотеки std, где я не нахожу никакого конструктора на основе списка инициализаторов std::array. Кроме того, cppreference сообщает мне, что его можно инициализировать с помощью aggregate-initialization - что даже не похоже на обычный конструктор. Итак, есть ли способ создать конструктор для моего класса, который правильно перенаправляет инициализацию с желаемым синтаксисом члену std::array?

1 Ответ

1 голос
/ 27 мая 2020

С отправкой тегов:

template <typename T>
struct type_identity { using type = T; };

template <typename Type, typename VectorType = std::vector<Type>>
class MathVector
{
public:
    MathVector(std::initializer_list<Type> il)
        : MathVector(il, type_identity<VectorType>{}) {}

private:
    template <typename T>
    MathVector(std::initializer_list<Type> il, type_identity<T>)
        : vector(il) {}

    template <typename T, std::size_t N>
    MathVector(std::initializer_list<Type> il, type_identity<std::array<T, N>>)
        : MathVector(il, std::make_index_sequence<N>{}) {}

    template <std::size_t... Is>
    MathVector(std::initializer_list<Type> il, std::index_sequence<Is...>)
        : vector{ *(il.begin() + Is)... } {}

    VectorType vector;
};

DEMO


Альтернативным решением является использование варианта c конструктор шаблонов

template <typename Type, typename VectorType = std::vector<Type>>
class MathVector
{
public:
    template <typename... Ts>
    MathVector(Ts&&... ts)
        : vector{ std::forward<Ts>(ts)... } {}

    MathVector(MathVector& rhs)
        : MathVector(const_cast<const MathVector&>(rhs)) {}

    MathVector(const MathVector& rhs)
        : vector(rhs.vector) {}

private:
    VectorType vector;
};

DEMO 2

или короче в :

template <typename Type, typename VectorType = std::vector<Type>>
class MathVector
{
public:
    MathVector(std::convertible_to<Type> auto&&... ts)
        : vector{ std::forward<decltype(ts)>(ts)... } {}

private:
    VectorType vector;
};

ДЕМО 3

...