Пересылка списка инициализаторов/агрегированной инициализации члену std::array

У меня есть класс, который инкапсулирует тип контейнера, совместимый с 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?


person PluginPenguin    schedule 27.05.2020    source источник


Ответы (1)


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

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;
};

ДЕМО


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

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;
};

ДЕМО 2

или короче в

ДЕМО 3

person Piotr Skotnicki    schedule 27.05.2020