避免在C ++(部分)模板专业化中重复代码

时间:2014-08-20 13:11:13

标签: c++ templates

我有一个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函数的实现(以及所有类似函数的实现)吗? 如果我不能用这种方式解决问题,你对如何使用动态和静态内存分配创建Vector类有其他想法吗?

2 个答案:

答案 0 :(得分:3)

我会选择基于策略的设计,类似于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.");
}

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

答案 1 :(得分:1)

我会做类似以下的事情,即只专注于数据部分:

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