Шаблоны: как управлять числом конструкторов args с использованием переменной шаблона.
Я пытаюсь сделать простой векторный класс (математика) следующим образом:
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
is >= 1, GetY()
, если D
is >= 2 и GetZ()
, если D
is >= 3, но следующий код должен генерировать время компиляции ошибка:
Vector<2> v(1.0f, -6.3f);
cout << v.GetZ() << endl;
Как сгенерировать ошибку времени компиляции, если D является < 1
Я не следую никакому конкретному стандарту, все будет работать для меня.
Ответы
Ответ 1
У меня нет доступа к компилятору С++ 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]; }
};
Ответ 2
Итак, я представил немного глупый ответ, который понравился людям. Но это намного проще, чем это:)
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];
};
Вы можете использовать variadic templates и 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
Ответ 3
Это немного не по теме, но, возможно, это будет наименьший объем работы: используйте tuple
. Затем вы бесплатно получаете все функции доступа.
Осталось только сделать кортеж factory:
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;
Ответ 4
Это должно выполнить задание:
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;
}
Ответ 5
Как я могу объявить функцию-конструктор для принятия D-аргументов типа T?
Вы не можете этого сделать. Вы можете специализироваться для каждого из поддерживаемых измерений и предоставлять подходящий конструктор для каждого из них. Или вы можете определить конструктор, который принимает несколько аргументов по умолчанию и игнорирует те, которые не используются. Или вы можете определить несколько конструкторов: от 1 до некоторого верхнего предела и static_assert
, если количество аргументов больше, чем D
.
Как добавить функцию только в том случае, если D - конкретный номер?
Это делается со специализацией. Вам нужно будет перенести всю общую функциональность на некоторый шаблон VectorBase и наследовать от нее, а также добавить частичную специализацию по размерности для добавления этих функций.
Как сгенерировать ошибку времени компиляции, если D является < 1
Или вы можете определить все эти функции для базового шаблона, а static_assert
, если D
недостаточно для использования этой функции. Однако вы только что потеряли явный экземпляр. Вы также можете добавить параметр типа фиктивного шаблона к таким функциям, поэтому вы можете использовать enable_if
и SFINAE для отбрасывания функций.