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