C++ 这种抽象模板矩阵类数据类型的方法好吗?

C++ 这种抽象模板矩阵类数据类型的方法好吗?,c++,oop,C++,Oop,我最近不得不写我自己的矩阵乘法库。最初,我将其作为模板类编写,然后我意识到大多数类使用matrix类,而不关心matrix类使用的数据类型,因为它们只执行特定的转换,而不检查结果。所以他们真的不应该知道数据类型。我在考虑创建一个矩阵类,其中包含一个指向数据的空指针 class Mat { private: void *data; int dtype; // data type used by matrix int cols,

我最近不得不写我自己的矩阵乘法库。最初,我将其作为模板类编写,然后我意识到大多数类使用matrix类,而不关心matrix类使用的数据类型,因为它们只执行特定的转换,而不检查结果。所以他们真的不应该知道数据类型。我在考虑创建一个矩阵类,其中包含一个指向数据的空指针

    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
    };
class-Mat
{
私人:
作废*数据;
int dtype;//矩阵使用的数据类型
int列,行;
模板
Mat add(const Mat&a,键入unused);//注意未使用的参数
公众:
Mat(int-dtype);
~Mat();
Mat操作员+(常数Mat&a);
模板
键入*getdata();//这是唯一一个公开
//由于用户希望读取元素,因此向用户发送数据类型
};
我需要一个加法函数作为模板,因为它使用SSE内部函数加速计算,并且我已经使用模板类抽象了内部函数。因此,我考虑在模板add中添加一个未使用的参数,以便编译器能够区分不同的模板

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;
}
Mat::operator+(常量Mat&a)
{
Mat结果;
开关(数据类型)
{
案例0://int
结果=此->添加(a,0);
打破
案例1://浮动
结果=此->添加(a,0);
打破
};
返回结果;
}
这是个坏主意吗?如果没有任何方法可以去掉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
}
vector transformVector;//在矩阵上执行操作的类的列表
矢量结果;//中间结果向量
结果。推回(输入垫)
对于(int i=0;itransform(results[i]);
//这里的transform可能必须返回float类型的结果
//即使输入的类型是int
}

Mat
类模板化并让编译器创建必要的add函数将更有效

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

您甚至可以创建一个模板,用于将
Mat
添加到
Mat
(或两个其他不同类型的矩阵)

模板
垫子{
std::array data;//或其他数据结构
// ...
模板
添加(施工材料和其他);
};

Mat
类模板化并让编译器创建必要的add函数将更有效

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

您甚至可以创建一个模板,用于将
Mat
添加到
Mat
(或两个其他不同类型的矩阵)

模板
垫子{
std::array data;//或其他数据结构
// ...
模板
添加(施工材料和其他);
};

这里的一个难点是
键入*getData()
。 这里再次说明,要么返回一个普通的
void*
,并要求调用方对其执行显式强制转换,要么必须使用模板函数

长话短说,您已经更改了一个模板类(重载在编译时解决),用于一组模板方法和一些开关,以在运行时解决一些函数


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

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

编辑之后,我可以看到您希望在单个容器中存储不同底层类型的矩阵。如果所有矩阵都可以从一个普通的非模板类型派生,我可以想象多态性,但是如果您稍后突然陷入
type*getData()
问题,我也不会感到惊讶:您静态强制转换了一个空指针,因此编译器无法阻止您执行错误的强制转换。另一种可能是矩阵上的
std::variant
(如果是C++17)或
boost::variant
或任何其他变体或任何替代方案。其中一些实现了在运行时防止错误转换的技巧

如果不对实际问题进行试验,就很难知道哪种方法是最好的


其他一些语言,如Java,没有模板(每个实现都有一个不同的类),而是泛型(一个作用于对象的公共类)。优点是只有一个类,因此正确的模板在链接时不可用的问题已经消失,缺点是需要一些技巧才能使实际类型在运行时可用。

这里有一个难点<
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);
};