我想实现具有多个数值运算的数字Vector
。
我使用模板来定义矢量的通用版本。
但实施应该与Vector3
等具体实施不同。
例如,+
操作看起来像这些。
Vector3实施
Vector3 operator+(const Vector3& vec3) const{
return Vector3(data[0]+vec3.data[0], data[1]+vec3.data[1], data[2]+vec3.data[2]);
}
模板实施
GenericVector operator+(const GenericVector<T,N>& vec) const{
GenericVector temp = *this;
for(int i=0;i<N;i++)
{
temp.data[i] += vec.data[i];
}
return temp;
}
现在我们无法知道它会有多少data
,我们无法定义像Vector3(x,y,z)
这样的构造函数。这就是我在+ operator
重载中引入循环的原因。
实际上,当涉及到代码的出现时,Vector3
实现看起来更好。但它不能缩放到不同大小的矢量。
模板的实现看起来不如Vector3
好。但是模板版本可以通过这样的方式轻松扩展到许多大小的向量。
typedef GenericVector<double, 3> Vector3d;
typedef GenericVector<double, 6> Vector6d;
我不熟悉C ++的模板。是否有人可以指出我的实施或理解问题?
我的问题是,在模板版本+ operator
的实现中,选择引入for循环以适应未知整数N
正确或唯一的选择?
简单的一个问题,
我可以避免+ operator
中的for循环吗?
这是我的部分代码。
template <typename T, int N>
class GenericVector{
protected:
T data[N];
public:
GenericVector()
{
for(int i=0;i<N;i++)
{
data[i] = static_cast<T>(0);
}
}
GenericVector operator+(const GenericVector<T,N>& vec) const{
GenericVector temp = *this;
for(int i=0;i<N;i++)
{
temp.data[i] += vec.data[i];
}
return temp;
}
//...
};
答案 0 :(得分:3)
避免各种操作中的循环的一种方法是不存储数组,但使用继承和特化来处理不同数量的参数。有点像这样:
template <typename T, int Size> class GVector;
template <typename T>
class GVector<T, 0> {
public:
GVector() {}
GVector& operator+= (GVector const&) { return *this; }
GVector operator+ (GVector const& other) const {
return GVector() += other;
}
// ...
};
template <typename T, int Size>
class GVector: public GVector<T, Size - 1> {
T value;
public:
GVector(): GVector<T, Size - 1>(), value() {}
template <typename Head, typename... Tail>
GVector(T const& head, Tail const&... tail)
: GVector<T, Size - 1>(tail...)
, value(head) {
}
GVector& operator+= (GVector const& other) {
this->value += other.value;
this->GVector<T, Size - 1>::operator+= (other);
return *this;
}
GVector operator+ (GVector const& other) const {
return GVector(*this) += other;
}
// ...
};
答案 1 :(得分:1)
在使代码更通用时通常需要权衡:当您可以使用不同的参数重用通用代码时,可以减少代码重复,但这通常意味着代码比非通用版本更复杂,并且还限制了优化的机会。更好的编译器和更新的C ++功能有助于减少权衡,但它们仍然存在。
要回答您的问题,是的,可以创建一个不使用for
循环迭代向量元素的通用向量类。使用for
循环是可接受的实现,但是在我使用generic vector class的实验中,我选择不使用它们,因为我发现我的编译器即使在优化版本中也没有完全展开。
注意 - 此代码主要用于研究构建泛型向量类的权衡。我不一定主张在生产中使用这样的设计。
C ++ 11 variadic templates为您的构造函数问题提供了解决方案,尽管它们存在某些问题:
template <typename T, size_t N>
class Vector {
public:
static const size_t dimension = N;
Vector() = default;
Vector(const Vector&) = default;
template <typename... Ts>
Vector(T t, Ts&&... ts)
: aw({t, std::forward<Ts>(ts)...}) {
static_assert(sizeof...(Ts) == N - 1, "Constructor must be passed N initializers.");
}
private:
struct ArrayWrapper {
T e_[N];
} aw; // ArrayWrapper lets us initialize in constructor initializer
};
它们还可用于避免在运算符中循环:
template <size_t I, typename F, typename... Args>
auto apply(F f, Args&&... args) {
return f(args.e(I)...);
}
template <typename F, size_t... Is, typename... Args>
auto apply(F f, std::index_sequence<Is...>, Args&&... args) {
using vec = std::common_type_t<Args...>;
using resvec = Vector<decltype(apply<0>(f, args...)), vec::dimension>;
return resvec{apply<Is>(f, args...)...};
}
template <typename F, typename... Args>
inline auto memberwise(F f, const Args&... args) {
using vec = std::common_type_t<Args...>;
return apply(f, std::make_index_sequence<vec::dimension>{}, args...);
}
template <typename T, size_t N>
inline Vector<T, N> operator+(const Vector<T, N>& a, const Vector<T, N>& b) {
return memberwise(std::plus<>{}, a, b);
}
在我使用这种方法的实验中,我发现这可以在我的编译器的优化版本中生成良好的代码,但它在非优化版本中增加了相当多的开销,而不是使用for
循环。