通用矢量类实现

时间:2015-09-02 18:39:47

标签: c++ templates vector generic-programming

我想实现具有多个数值运算的数字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;
    }

    //...
};

2 个答案:

答案 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循环。