Шаблоны: как контролировать количество аргументов конструктора с помощью переменной шаблона. - PullRequest
17 голосов
/ 17 ноября 2011

Я пытаюсь создать простой класс Vector (математика) следующим образом:

template <int D, typename T = float>
class Vector
{
  T m[D];
  // ...
};

Где D - количество измерений. Если это два, вектор будет хранить два значения типа T.


Как я могу объявить функцию конструктора, которая будет принимать D аргументы типа T?

Vector<2> v(1.0f, -6.3f);

Как добавить функцию, только если D, если определенное число? Я хотел бы добавить GetX(), если D равен> = 1, GetY(), если D равен> = 2, и GetZ(), если D равен> = 3, но следующий код должен генерировать время компиляции ошибка:

Vector<2> v(1.0f, -6.3f);
cout << v.GetZ() << endl;

Как создать ошибку времени компиляции, если D <1? </p>

Я не придерживаюсь какого-то определенного стандарта, у меня все будет работать.

Ответы [ 5 ]

19 голосов
/ 17 ноября 2011

Так что я дал немного глупый ответ, который понравился людям.Но это намного проще, чем это:)

template <int D, typename T = float>
class v {
public:
    template <typename... Args>
    v(Args... args) : a{ T(args)... } {
        static_assert(sizeof...(Args) == D, "wrong number of arguments");
    }

private:
    T a[D];
};

Вы можете использовать шаблоны с переменными параметрами и SFINAE, чтобы получить конструктор с нужным количеством параметров.

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

Это означает, что нам нужно использовать SFINAE для первого параметра.

Тогда это означает, что пакет параметров послепервый параметр должен иметь на один параметр меньше размеров.

Имея это в виду, мы можем написать:

template <int D, typename T>
class v {
public:
    template <typename... Tail>
    v(typename std::enable_if<sizeof...(Tail)+1 == D, T>::type head, Tail... tail)
    : a{ head, T(tail)... } {}

private:
    T a[D];
};

А теперь:

v<4, int> a(1,2,3,4); // ok!
v<4, int> b(1,2,3);   // error! no such constructor
13 голосов
/ 17 ноября 2011

У меня нет доступа к компилятору C ++ 11, но может быть что-то подобное может работать?

#include <array>
#include <type_traits>

template <int D, typename T>
class Vector
{
    static_assert(D > 0, "Dimension must be greater than 0");
    std::array<T,D> m;
public:
    template<typename... Args>
    Vector(Args&&... args) : m{T(args)...}
    {
         static_assert(sizeof...(Args) == D, "Invalid number of constructor arguments.");
    }

    T GetX() const { return m[0]; }
    T GetY() const { return m[1]; }
    T GetZ() const { return m[2]; }
};

template <typename T>
class Vector<1, T>
{
    std::array<T,1> m;
public:
    Vector(const T& t0) : m{t0}
    {
    }

    T GetX() const { return m[0]; }
};

template <typename T>
class Vector<2, T>
{
    std::array<T,2> m;
public:
    Vector(const T& t0, const T& t1) : m{t0, t1}
    {
    }

    T GetX() const { return m[0]; }
    T GetY() const { return m[1]; }
};
5 голосов
/ 17 ноября 2011

Это немного не по теме, но, возможно, это будет наименьшее количество работы: используйте tuple. Тогда вы получите все функции доступа бесплатно.

Осталось только создать фабрику кортежей:

template <typename T, unsigned int D> struct tuple_maker
{
  typedef typename tcat<T, tuple_maker<T, D - 1>::type>::type type;
}
template <typename T> struct tuple_maker<T, 1>
{
  typedef std::tuple<T> type;
}

Нам нужен вспомогательный tcat:

template <typename T, typename Tuple> struct tcat;
template <typename T, typename ...Args> struct tcat<T, std::tuple<Args...>>
{
  typedef typename std::tuple<T, Args...> type;
}

Использование:

tuple_maker<float, 3>::type myVec3D;

С помощью псевдонимов шаблонов мы можем сделать один лучше:

template <typename T, unsigned int D>
using MyVector = typename tuple_maker<T, D>::type;

MyVector<double, 4> myVec4D;
2 голосов
/ 17 ноября 2011

Это должно сделать работу:

template<int N, typename T>
class Array
{
private:
    T Data[N];

public:
    template<int i>
    void Init() { }
    template<int i, typename... Args>
    void Init(T Arg0, Args... Rest)
    {
        Data[i] = Arg0;
        Init<i + 1>(Rest...);
    }
    template<typename... Args>
    Array(T Arg0, Args... Rest)
    {
        static_assert(sizeof...(Args) + 1 == 5, "Wrong number of arguments");
        Data[0] = Arg0;
        Init<1>(Rest...);
    }
};

int main (int argc, const char * argv[])
{
    Array<5, int> arr(1, 2, 3, 4, 5);
    return 0;
}
1 голос
/ 17 ноября 2011

Как я могу объявить функцию конструктора, чтобы принимать D аргументов типа T?

Ты не можешь этого сделать. Вы можете специализироваться для каждого из поддерживаемых измерений и предоставить соответствующий конструктор для каждого из них. Или вы можете определить конструктор, который принимает несколько значений по умолчанию и игнорировать те, которые не используются. Или вы можете определить несколько конструкторов, от 1 до некоторого верхнего предела, и static_assert, если число аргументов превышает D.

Как добавить функцию, только если D, если конкретный номер?

Это делается со специализацией. Вам нужно было бы переместить все общие функции в какой-либо шаблон VectorBase и унаследовать его, а также частично настроить размерность, чтобы добавить эти функции.

Как создать ошибку времени компиляции, если D <1? </p>

Или вы можете определить все эти функции для базового шаблона, и static_assert, если D недостаточно для использования этой функции. Теперь вы только что потеряли явную реализацию. Вы также можете добавить фиктивный параметр шаблона для таких функций, так что вы можете использовать enable_if и SFINAE для отмены функций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...