这种抽象模板Matrix类数据类型的方式好吗?

时间:2018-06-12 04:44:20

标签: c++ oop

我最近不得不编写自己的矩阵乘法库。最初,我把它写成模板类,然后我意识到大多数类使用矩阵类而不关心Matrix类使用的数据类型,因为它们只是执行某个转换并且不检查结果。所以他们真的不应该知道数据类型。我正在考虑制作一个带有指向数据的void指针的矩阵类。

    class Mat
    {
    private:
        void *data;
        int dtype; // data type used by matrix
        int cols, rows;

        template<class type>
        Mat add(const Mat& a, type unused); // notice unused parameters

    public:
        Mat(int dtype);
        ~Mat();
        Mat operator+(const Mat& a);
        template<class type>
        type* getdata(); // this only function  that exposes the 
        //datatype to the user since they want to read the elements
    };

我需要一个添加函数作为模板,因为它使用SSE内在函数加速计算,我使用模板类抽象了内在函数。所以我想到将未使用的参数添加到模板添加中,以便编译器能够区分不同的模板。

Mat Mat::operator+(const Mat& a)
{
    Mat result;

    switch(dtype)
    {
        case 0: // int
            result = this->add<int>(a, 0);
            break;
        case 1: // float
            result = this->add<float>(a, 0);
            break;
    };

    return result;
}

这是个坏主意吗?如果没有办法摆脱add方法中未使用的参数?

我的另一个想法是创建IntMatrix,Float Matrix类继承自Mat类,只是为了让它使用模板类型调用add函数,以避免在添加的操作符重载中进行case切换。这也是一个糟糕的设计吗?

澄清

我希望能够拥有2个向量:

vector<Transform*> transformVector; // list of classes doing operation on matrix
vector<Mat*> results; // intermediate results vector

results.push_back(input_mat)
for(int i = 0; i < transformVector.size(); ++i){
    results.push_back(transformVector[i]->transform(results[i]));
// transform here might have to return a result of type float
// even though the input was of type int
}

2 个答案:

答案 0 :(得分:1)

使Mat类模板化并让编译器创建必要的添加函数会更有效。

使用当前的实现,您必须为每种新类型添加一个新的switch case,并且要小心地将void*正确地转换为正确的类型。使用模板时,编译器会通过检查您的类型来帮助您。

您甚至可以创建一个模板,以便将Mat<int>添加到Mat<float>(或其他两种不同类型的矩阵)。

template <typename T, size_t Col, size_t Row>
Mat {
    std::array<T, Col * Row> data; // or other data structure
    // ...
    template <typename OtherT>
    add(const Mat<OtherT, Col, Row>& other);
};

答案 1 :(得分:1)

这里的一个难点是type * getData()。 在这里,要么返回一个普通的void *并要求调用者对其进行显式强制转换,要么必须使用模板化函数。

很长一段时间,你已经改变了一个模板化的类(在编译时解决了重载),一组模板方法和一些开关在运行时解决了一些函数。

你说大多数类使用矩阵类而不关心数据类型。这正是模板的用途:一堆独立于底层类型的存储和处理(模板可以做多一点,但最初是为此创建的)

void *始终是一个安全指针,是C兼容API的绝佳选择。但除非你遇到性能问题(模板可以在微小系统上使用太多内存,因为它们为每个实现声明了一个不同的类(*)),并且可以证明void *对于特定用例更好,你应该坚持共同的规则。编写简单易读的代码,只有在找到瓶颈时才进行优化。

编辑之后,我可以看到您想要将不同基础类型的矩阵存储在单个容器中。如果所有矩阵都可以从一个常见的非模板化类型派生出来,我可以想象多态性,但是如果你以后突然陷入 type * getData() 问题我就不会感到惊讶:你静态地抛出了一个无效指针,所以编译器无法阻止您进行错误的转换。另一种可能是std::variant矩阵(如果是C ++ 17)或boost::variant或任何其他变体任何替代品。其中一些实现了在运行时防止错误转换的技巧。

如果不试验真正的问题,很难知道哪种方式最好......

其他一些语言(如Java)没有 templates (每个实现的不同类)但是泛型(一个作用于对象的公共类)。专业人员只有一个类,所以关于链接时不可用的正确模板的问题已经消失,缺点是需要一些技巧才能在运行时提供实际类型。