我最近不得不编写自己的矩阵乘法库。最初,我把它写成模板类,然后我意识到大多数类使用矩阵类而不关心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
}
答案 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 (每个实现的不同类)但是泛型(一个作用于对象的公共类)。专业人员只有一个类,所以关于链接时不可用的正确模板的问题已经消失,缺点是需要一些技巧才能在运行时提供实际类型。