当前位置: 动力学知识库 > 问答 > 编程问答 >

c++ - Generic Vector Class Implementation

问题描述:

I want to implement numerical Vector which has several numerical operations.

I used template to define generic version of vector.

But implementation should be different with concrete implementation like Vector3.

For example, + operation would look like these.

Vector3 implementation

Vector3 operator+(const Vector3& vec3) const{

return Vector3(data[0]+vec3.data[0], data[1]+vec3.data[1], data[2]+vec3.data[2]);

}

template implementation

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;

}

Now that we cannot know how many data it would have, we cannot define constructor like Vector3(x,y,z). That's why I introduced for loop in + operator overloading.

Actually when it comes to appearance of codes, Vector3 implementation looks better. But it cannot be scaled to different size vectors.

Template's implementation looks less nicer than that of Vector3. But template version can easily scaled to many size vectors by doing like this.

typedef GenericVector<double, 3> Vector3d;

typedef GenericVector<double, 6> Vector6d;

I'm not used to template of C++. Is there any one who can point problem of my implementation or understandings?

My question is that in implementation of + operator in template version, is the choice of introducing for loop for accommodating unknown integer N right or the only choice?

To simple one question,

Can I avoid for loop in + operator?

This is parts of my codes.

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;

}

//...

};

网友答案:

One approach to avoid the loops in the various operations is to not store an array but use inheritance and specialization to deal with the different number of arguments. Something along the lines of this:

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;
    }
    // ...
};
网友答案:

There's generally a tradeoff when making code more generic: you reduce code duplication when you can reuse your generic code with different parameters but it usually means somewhat more complex code than the non-generic version and also limits opportunities for optimization. Better compilers and newer C++ features help reduce the tradeoffs but they still exist.

To answer your question, yes it is possible to create a generic vector class that doesn't use for loops to iterate over the elements of the vector. Using for loops is an acceptable implementation, however in my experiments with a generic vector class I opted not to use them because I found with my compiler that they were not fully unrolled even in optimized builds.

Note - this code is mostly an experiment to investigate the tradeoffs in building a generic vector class. I wouldn't necessarily advocate using a design like this in production.

C++11 variadic templates offer a solution to your constructor problem although there are certain issues that come along with them:

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
};

They can also be used to avoid looping in operators:

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);
}

In my experiments with this approach I found this produces good code in optimized builds with my compiler but it adds quite a lot of overhead in non-optimized builds, more than using a for loop.

分享给朋友:
您可能感兴趣的文章:
随机阅读: