Специализированный конструктор для класса Vector

Я пытаюсь написать математический векторный класс. Первая версия выглядит так:

  template <typename T, unsigned int n>
    struct Vector {
    Vector<T, n>(T t = T()) // default
    {
        for (int i = 0; i < n; i++)
        {
            data[i] = t;
        }
    }

    Vector<T, n>(const Vector<T, n> &aVector)
    {
        for (unsigned int i = 0; i < n; i++)
        {
            data[i] = aVector.data[i];
        }
    }

    Vector<T, n>(const T arr[n])
    {
        for (unsigned int i = 0; i < n; i++)
        {
            data[i] = arr[i];
        }
    }

    T& operator[](unsigned int i);
    const T& operator[](unsigned int i) const;
    Vector<T, n>& operator=(const Vector<T, n> &aVector);

    void normalise();   

    T data[n]; 
    };

У меня также есть операторы (+, * и т. д.), объявленные вне класса, а также несколько других классов.

Однако вот в чем дело. Для векторов длины 2, 3, 4 я хотел бы иметь конструктор (или функцию), который принимает x, y (для Vec2), x, y, z или x, y, z, w в качестве параметров.

Однако, кажется, что вы не можете сделать специализированные конструкторы для этой цели. Как мне поступить в таком случае? Должен ли я полностью специализироваться на трех случаях? Разве это не означает, что я должен переписать куски кода?

У меня также есть аналогичный класс Matrix ( Matrix ), и я уверен, что мне понадобятся некоторые конструкторы для вращения, перевода, масштабирования и т. д. Я предполагаю, что мне нужно будет решить аналогичную проблему.

Если ты видишь

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

Однако вот в чем дело. Для векторов длины 2, 3, 4 я хотел бы иметь конструктор (или функцию), который принимает x, y (для Vec2), x, y, z или x, y, z, w в качестве параметров.

Однако, кажется, что вы не можете сделать специализированные конструкторы для этой цели. Как мне поступить в таком случае? Должен ли я полностью специализироваться на трех случаях? Разве это не означает, что я должен переписать куски кода?

У меня также есть аналогичный класс Matrix ( Matrix ), и я уверен, что мне понадобятся некоторые конструкторы для вращения, перевода, масштабирования и т. д. Я предполагаю, что мне нужно будет решить аналогичную проблему.

Если вы видите в коде что-то, что кажется вам неправильным, не стесняйтесь указать на это.

РЕДАКТИРОВАТЬ: В случае, если я был недостаточно ясен, массивы должны быть одномерными, и все его компоненты имеют один и тот же тип. Специализации предназначены для массивов с 2, 3 и 4 элементами.


person spliblib    schedule 24.11.2015    source источник
comment
Кстати, вашему классу Vector не нужен определяемый пользователем конструктор копирования или оператор присваивания.   -  person PaulMcKenzie    schedule 24.11.2015
comment
For vectors of length 2, 3, 4 Вы имеете в виду, что количество размеров может быть 2, 3 или 4?   -  person PaulMcKenzie    schedule 24.11.2015
comment
Массивы внутри структур можно копировать без дополнительной помощи. Таким образом, определяемый пользователем конструктор копирования/операция присваивания не требуется.   -  person PaulMcKenzie    schedule 24.11.2015
comment
Почему бы просто не отправить std::vector (не то же самое, что Vector) значений, где vector::size() определяет, есть ли у вас 2, 3, 4 или n элементов?   -  person PaulMcKenzie    schedule 24.11.2015
comment
Потому что я не хочу проверять размер каждый раз, когда что-то делаю с векторами или матрицами.   -  person spliblib    schedule 24.11.2015


Ответы (3)


Вы можете использовать вариативный шаблон:

#include <chrono>
#include <future>
#include <iostream>
#include <stdexcept>


template<typename T, unsigned int n>
struct Vector
{
    // Note: We need x and y:
    // The default (in the question) is a constructor taking a single argument.
    template <typename ... Args>
    Vector(T x, T y, Args ... args)
    {
        static_assert(sizeof ... (Args) == n - 2, "Invalid number of arguments");
        auto initializer_list = { x, y, std::forward<Args>(args)... };
        std::copy(initializer_list.begin(), initializer_list.end(), data);

    }

    T data[n];
};


template<typename T, unsigned int n>
void print(const Vector<T, n>& v) {
    for(unsigned i = 0; i < n; ++i)
        std::cout << v.data[i] << ' ';
    std::cout << '\n';
}


int main()
{
    Vector<int, 2> v2(1, 2);
    Vector<int, 3> v3(1, 2, 3);
    Vector<int, 4> v4(1, 2, 3, 4);
    print(v2);
    print(v3);
    print(v4);

    // Invalid number of arguments
    // Vector<int, 3> e2(1, 2);

    // Invalid number of arguments
    // Vector<int, 3> e4(1, 2, 3, 4);
    return 0;
}
person Community    schedule 24.11.2015

Лично я бы объявил разные классы для каждого вектора. Vec2<T1, T2>, Vec3<T1, T2, T3> и Vec4<T1, T2, T3, T4>. Сделайте так, чтобы каждый класс Vec* публично наследовал базовый класс Vector без шаблона, а затем создайте статическую функцию create в Vector с перегрузкой для каждого типа Vec*, который будет отвечать за их создание. Псевдопример для Vec3:

template<T1, T2, T3> static std::shared_ptr<Vector> create(T1 a1, T2 a2, T3 a3)
{
    return new Vec3<T1, T2, T3>(a1, a2, a3);
}
auto vec = Vector::create<int, int, int>(1, 2, 3);
person Nick Cano    schedule 24.11.2015

вариативный шаблон и SFINAE могут решить эту проблему. но я думаю, что проще было бы наследовать от (специализированного) помощника

template <typename T, unsigned int n>
struct VectorHelper<T, n>
{
    T data[n];
};

template <typename T>
struct VectorHelper<T, 2>
{
    VectorHelper(T x, T y) : {data[0] = x; data[1] = y;}
    T data[2];
};

template <typename T>
struct VectorHelper<T, 3>
{
    VectorHelper(T x, T y, T z) : {data[0] = x; data[1] = y; data[2] = z}
    T data[3];
};


template <typename T, unsigned int n>
struct Vector : private VectorHelper<T, n>
{
    using VectorHelper<T, n>::VectorHelper; // use base constructor(s)
    // your previous implementation without `data` (as member of base)
};
person Jarod42    schedule 24.11.2015