【问题标题】:Generic Vector Class Implementation通用向量类实现
【发布时间】:2015-09-02 18:39:46
【问题描述】:

我想实现数字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重载中引入for循环的原因。

实际上,在代码外观方面,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;
    }

    //...
};

【问题讨论】:

  • 那么问题是什么?它不起作用还是您在问这是否是正确的方法?
  • 您不需要在循环中将data 初始化为0。您只需要GenericVector(): data() {}。而且您不需要operator+ 参数中的模板参数。这样做:GenericVector operator+(const GenericVector&amp; vec) const。除此之外,它看起来还可以。但我不知道你在问什么。
  • 另外,您可能应该实现GenericVector&amp; operator+=(const GenericVector&amp;) 并在operator+ 中使用它。
  • 谢谢,@juanchopanza。我确实实现了 '+=' 但为了简单起见这里省略:D.

标签: c++ templates vector generic-programming


【解决方案1】:

避免各种操作中出现循环的一种方法是存储数组,而是使用继承和专门化来处理不同数量的参数。类似这样的东西:

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 的向量的特化。
  • 不确定这是否是任何东西的实际收益。
  • 谢谢,我正在努力理解您的回答。顺便说一句,... 中的 GVector 构造函数是什么?我从没见过那些。
  • @JaeJunLEE,我想,在这个阶段你真的不需要遵循这个例子。迪特玛有点拖钓。
  • @DietmarKühl,哦,是的。我不知何故忽略了整个代码。至于拖钓,考虑到所谓的 OP 熟练程度,以及理解答案所需的熟练程度,存在巨大差距。
【解决方案2】:

在使代码更通用时通常需要权衡:当您可以重用具有不同参数的通用代码时,您可以减少代码重复,但这通常意味着比非通用版本更复杂的代码,并且也限制了优化的机会。更好的编译器和更新的 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 循环更多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-05
    • 1970-01-01
    • 1970-01-01
    • 2021-03-01
    • 2015-01-25
    • 1970-01-01
    • 2021-08-17
    • 1970-01-01
    相关资源
    最近更新 更多