我想实现一个抽象的Matrix(模板)类,并首先实现一个惰性实现。后来我想实现这个类的更加性能导向的版本,并希望在我的整个项目中使用它而不改变所有内容。
目前的问题是,我在实施+ -operator时遇到了问题。
下面的代码是一次迭代,但我尝试了许多不同的可能性。但是我得到一个C2259“无法创建抽象类的实例”,如下例所示,或者我遇到运行时问题(返回引用或指针时发生访问冲突)。
我确信我错过了一个容易和愚蠢的观点(再次)。
AbstMatrix.cpp:
#pragma once
#include "stdafx.h"
#include "Matrix.hpp"
template<typename T>
class AbstMatrix // : public AddMultEnabled<AbstMatrix<T>>
{
public:
inline virtual size_t getNRows() const = 0;
inline virtual size_t getNCols() const = 0;
inline size_t getNEle() const { return this->getNCols() * this->getNRows(); }
inline virtual T get(size_t iRow, size_t iCol) const = 0;
inline virtual void set(size_t iRow, size_t iCol, T val) = 0;
// Element wise addition
virtual AbstMatrix<T>& operator+=(const AbstMatrix<T>& obj) {
cout << "AM: op+=" << endl;
if (this->getNRows() != obj->getNRows()
|| this->getNCols() != obj->getNCols()) {
throw "Matricies unequal";
}
for (size_t i = 0; i < this->getNRows(); i++) {
for (size_t j = 0; j < this->getNCols(); j++) {
this->set(i, j, this->get(i, j) + obj->get(i, j));
}
}
return *this;
}
// Elementwise addition
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const {
cout << "AM: op+" << endl;
Matrix<T> retM(*this);
return retM += obj;
}
};
Matrix.cpp:
#pragma once
#include "stdafx.h"
#include <algorithm>
#include "AbstMatrix.hpp"
template<typename T>
class Matrix : public AbstMatrix<T>
{
protected:
size_t nRows;
size_t nCols;
size_t nEle;
T* dat;
public:
Matrix(size_t nRows, size_t nCols, T defVal = 0) {
this->nRows = nRows;
this->nCols = nCols;
this->nEle = nCols*nRows;
this->dat = new T[this->getNEle()];
std::fill_n(this->dat, this->getNEle(), defVal);
}
Matrix(const AbstMatrix& obj) {
cout << "M: abst cpy:" << &obj << endl;
this->nRows = obj.getNRows();
this->nCols = obj.getNCols();
this->nEle = obj.getNEle();
this->dat = new T[this->getNEle()];
for (size_t i = 0; i < this->getNRows(); i++) {
for (size_t j = 0; j < this->getNCols(); j++) {
this->set(i, j, obj.get(i, j));
}
}
}
Matrix & operator= (const AbstMatrix & obj) {
this->nRows = obj.getNRows();
this->nCols = obj.getNCols();
this->nEle = obj.getNEle();
this->dat = new T[this->getNEle()];
for (size_t i = 0; i < this->getNRows(); i++) {
for (size_t j = 0; j < this->getNCols(); j++) {
this->set(i, j, obj.get(i, j));
}
}
}
~Matrix() { if (this->dat) delete[] this->dat; }
inline size_t getNRows() const { return this->nRows; }
inline size_t getNCols() const { return this->nCols; }
inline size_t getNEle() const { return this->nEle; }
inline T get(size_t iRow, size_t iCol) const {
cout << "M: get " << iRow << ", " << iCol << endl;
return this->dat[this->getIdx(iRow, iCol)];
}
inline void set(size_t iRow, size_t iCol, T val) {
cout << "M: set " << iRow << ", " << iCol << endl;
this->dat[this->getIdx(iRow, iCol)] = val;
}
inline AbstMatrix* clone() const {
cout << "M: clone " << endl;
return new Matrix(*this);
}
protected:
size_t getIdx(size_t iCol, size_t iRow) const {
cout << "M: getIdx " << iRow << ", " << iCol << ", "
<< (size_t) (this->getNCols() * iRow + iCol) << endl;
return this->getNCols() * iRow + iCol;
}
};
main.cpp中:
#include "stdafx.h"
#include "Matrix.hpp"
int main()
{
Matrix<float> a(5, 5);
Matrix<float> b(5, 5);
a + b;
return 0;
}
非常感谢你的帮助!
[编辑:]我修复了下面提到的(复制 - 粘贴)错误。 Matrix现在有一个副本和一个移动构造函数。我在AbstMatrix的底部添加了以下代码:
namespace detail {
template <typename T>
T AbstMatrix_ElemType(const AbstMatrix<T>&) { return T(); }
}
template <typename M1, typename M2>
auto operator+(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemType(obj2))>::value,
M1> {
return obj1 += obj2;
}
template <typename M1, typename M2>
auto operator*(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemType(obj2))>::value,
M1> {
return obj1 *= obj2;
}
// Mat multiplication
template <typename M1, typename M2>
auto mult(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemType(obj2))>::value,
M1> {
cout << "AM: mult" << endl;
if (obj1.getNCols() != obj2.getNRows()) {
throw("Matricies incompatible");
}
typedef decltype(detail::AbstMatrix_ElemType(obj1)) matValueType;
M1 retM(obj1.getNRows(), obj2.getNCols());
for (size_t i = 0; i < obj1.getNRows(); i++) {
for (size_t j = 0; j < obj2.getNCols(); j++) {
matValueType tmp = 0;
for (size_t x = 0; x < obj1.getNCols(); x++) {
tmp += obj1.get(i, x) * obj2.get(x, j);
}
retM.set(i, j, tmp);
}
}
return retM;
}
这对我来说很有效。遗憾的是,我仍然不明白为什么这段代码有效。我尝试在cppreference上阅读doc,但它让我很困惑。你有一个更容易理解代码的来源吗?
非常感谢@aschepler!
答案 0 :(得分:1)
// Elementwise addition
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const {
cout << "AM: op+" << endl;
Matrix<T> retM(*this);
return retM += obj;
}
您不能使用诸如AbstMatrix<T>
之类的抽象类作为返回类型,因为这涉及创建完全相同类型的对象。此外,您的operator+
实现依赖于特定的子类Matrix<T>
。通常,基类不应该知道它的派生类(除非你使用CRTP)。
相反,您可以在类外定义operator+
模板,该模板作用于继承AbstMatrix
相同特化的任意两个对象,并返回LHS类型:
#include <type_traits>
namespace detail {
template <typename T>
T AbstMatrix_ElemType(const AbstMatrix<T>&);
}
template <typename M1, typename M2>
auto operator+(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemTYpe(obj2))>::value,
M1>
{ return obj1 += obj2; }
答案 1 :(得分:0)
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const { cout << "AM: op+" << endl; Matrix<T> retM(*this); return retM += obj; }
您不能编写返回抽象类对象的运算符。简单地说,您的操作员说我的方法返回一个类型为AbstMatrix 的实例,但这样的实例不能存在于抽象类中。您只能拥有派生的,具体的(非抽象)类的实际实例,并持有引用(AbstMatrix<T>&
)或指针({{ 1}})。
您实际上是在创建派生类型AbstMatrix<T>*
的实例,但为了匹配函数的原型,Matrix<T>
语句被强制转换为return
实例(称为对象的机制)切片;它必须创建AbstMatrix<T>
的实例并调用复制构造函数。由于无法创建此类实例,因此会发生编译错误。
充其量,您可以让AbstMatrix<T>
返回operator+
个对象。但是,拥有一个抽象基类的整个想法,并非如此严格依赖于其中一个派生类,并不是一个好的设计理念。也许你应该重新思考设计并放弃抽象矩阵类的想法,但只能使它成为一个类模板。
答案 2 :(得分:0)
这样使用的抽象接口不适用于价值语义计算。
您无法返回抽象类的实例。实际实例 - 值 - 具有固定的存储大小和C ++类型。派生抽象接口的实例不会“适合”。
有几种方法可以解决这个问题。一种方法是实现自己的多态性。
从template<class T>class AbstMatrix
开始,就像你一样。摆脱operator
s - 仅公开纯虚拟方法。虚拟运营商“工作”,但使用起来很尴尬,所以不要打扰。包含virtual std::unique_ptr<AbstMatrix>clone()const=0
考虑删除其他虚拟方法;只包括你需要的那些。见下文。您需要at(x,y)
,clone()
,increment_by(AbstMatrix)
,mult_by(AbstMatrix)
等。
接下来写Matrix<T>
。这不会从AbstMatrix<T>
继承,而是拥有一个std::unique_ptr<AbstMatrix<T>> pImpl;
。它也可以从unique_ptr<AbstMatrix<T>>
构建。它可以是“空的”;它的方法不能假设pImpl
是非空的。
此Matrix<T>
类型实现operator+=
,operator+
,operator=
,Matrix(Matris const&)
,Matrix(Matris&&)=default
等。其工作是< em> value语义类型是多态的,因为它的行为是由它在一个唯一的ptr中拥有的抽象类决定的。
这使您可以拥有多态值类型。
现在,您的实施继承自AbstMatrix<T>
,并且可以存储在Matrix<T>
中。