避免c++(部分)模板专门化中的代码重复

Avoiding code repetition in C++ (partial) template specialization

本文关键字:代码 专门化 c++ 部分 避免      更新时间:2023-10-16

我有一个类Vector,它表示给定模板类的数学向量。我希望能够以两种不同的方式保存矢量:

template <class T>
class Vector
{
private:
    T* elements;
};

template <class T, unsigned int D>
class Vector
{
private:
    T elements[D];
};

在第一个例子中,我在构造函数和析构函数中使用new和delete来分配和释放数组。现在,由于我不想为两个类编写两次所有方法,而且由于我有两个同名但模板参数不同的类,因此它甚至不会以这种方式编译,因此我想将这两个类合并为一个类,如下所示:

template <class T, int D = -1>
class Vector
{
public:
    Vector<T, D> add(const Vector<T, D>& add) const;
private:
    T elements[D];
};
template <class T>
class Vector<T, -1>
{
public:
    Vector<T, D> add(const Vector<T, D>& add) const;
private:
    T* elements;
};

所以第二部分只是第一部分的部分模板专门化。如果没有给出维度,应该使用动态分配选项(D的默认参数)。作为一个例子,我添加了一个函数来计算两个向量的和。现在我的问题是,我必须给出两个实现逻辑上只是一个函数。每当我访问元素数组时,它在动态和静态Vector类中的语法完全相同。我能否以某种方式将这两种实现合并为add函数的一个实现(以及所有类似的函数)?如果我不能解决这种方式的问题,你有其他的想法,如何创建矢量类与动态和静态内存分配?

我会选择基于策略的设计,类似于std::vector处理分配的方式。

你的意思是:

    定义一个类来存储vector元素,但只提供一个最小的接口。( )
  • 定义独立于元素在策略中的存储方式的向量接口。它以独立于该实现的方式访问元素。策略类应该作为模板类型参数(它可以有一个默认值)添加,这样向量类的用户就可以选择使用哪个策略。继承策略类或添加其类型的成员(如果不想在公共接口中公开策略接口,则是私有的)。

示例(这里使用聚合而不是继承):

// The policy default implementation:
template <class T, int D>
class VectorStorage
{
    T elements[D];
public:
    T& operator[](int x) {
        return elements[x];
    }
    const T& operator[](int x) const {
        return elements[x];
    }
};
class VectorStorage<T, -1>
{
    T* elements; // (for allocation, see below)
public:
    T& operator[](int x) {
        return elements[x];
    }
    const T& operator[](int x) const {
        return elements[x];
    }
};

// The vector implementation, independent of the storage,
// but defaulting to the one above:
template <class T, int D = -1, class Storage = VectorStorage<T,D>>
class Vector
{
    Storage storage;
public:
    Vector<T, D> add(const Vector<T, D>& add) const {
        // Access your elements using "storage[x]"
    }
};

注意,需要为策略类提供合适的构造函数(对于动态存储类型,在构造过程中需要大小)。为所有的专门化提供一个唯一的构造函数接口,而不仅仅是为需要它的那个;并在vector的构造函数中适当地调用构造函数:

// within class VectorStorage<T,-1>:
VectorStorage(int size) : elements(new T[size]) {}
~VectorStorage() { delete[] elements; }
// within class VectorStorage<T,D>:
VectorStorage(int /* ignored */) {}
// within class Vector:
Vector(int size) : storage(size) {}

或者,为了支持像Vector<int,5> myVector;这样的客户端代码(即静态大小版本的默认构造函数),提供一个只允许在静态大小版本中调用的默认构造函数:

Vector() : storage(D) {
    static_assert(D != -1, "The default constructor is only allowed for the static-sized version of Vector.");
}

现在用户甚至可以使用Vectorstd::vector作为存储后端:Vector<int, -1, std::vector<int>>Vector<int, 5, std::vector<int>>。甚至是Vector<int, 5, std::array<int,5>>

我会做如下的事情,即只专门化数据部分:

template <class T, int D = -1>
class VectorData
{
public:
    int size() const { return D; }
protected:
    T elements[D];
};
template <class T>
class VectorData<T, -1>
{
public:
    explicit VectorData(int size) : elements(size) {}
    int size() const { return elements.size(); }
protected:
    std::vector<T> elements;
};

template <class T, int D = -1>
class Vector : protected VectorData<T, D>
{
public:
    using VectorData<T, D>::VectorData;
    Vector add(const Vector& add) const
    {
        Vector res(*this);
        for (int i = 0; i != this->size(); ++i) {
            res.elements[i] += add.elements[i];
        }
        return res;
    }
};