如何在抽象基类中实现运算符+



我想先实现一个抽象矩阵(模板)类并实现一个惰性实现。稍后,我想实现这个类的一个更面向性能的版本,并希望在我的整个项目中使用它,而无需更改所有内容。

当前的问题是,我在实现 + 运算符时遇到了问题。

下面的代码是一个迭代,但我尝试了许多不同的可能性。但是要么我得到一个 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;
}
};

矩阵.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;
}
};

主.cpp:

#include "stdafx.h"
#include "Matrix.hpp"
int main()
{
Matrix<float> a(5, 5);
Matrix<float> b(5, 5);
a + b;
return 0;
}

非常感谢您的帮助!

[编辑:] 我修复了下面提到的(复制粘贴)错误。矩阵现在有一个副本和一个移动构造函数。我在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;
} 

这对我来说非常有效。不幸的是,我仍然不明白为什么这段代码有效。我试图在 cpp偏好阅读文档,但它只是让我感到困惑。您是否有更简单的来源来理解代码?

非常感谢@aschepler!

// 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; }
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const {
cout << "AM: op+" << endl;
Matrix<T> retM(*this);
return retM += obj;
}

不能编写返回抽象类对象的此类运算符。简单地说,你的运算符说我的方法返回一个 AbstMatrix 类型的实例,但这样的实例对于抽象类来说不能存在。您只能拥有派生的具体(非抽象)类的实际实例,并在它们上保留引用(AbstMatrix<T>&)或指针(AbstMatrix<T>*)。

您实际上是在创建派生类型的实例Matrix<T>但为了匹配函数的原型,return语句被迫将其转换为AbstMatrix<T>实例(一种称为对象切片的机制;它必须创建AbstMatrix<T>的实例并调用复制构造函数)。由于它无法创建此类实例,因此会发生编译错误。

充其量,您可以operator+返回Matrix<T>对象。但是,拥有一个抽象基类的整个想法,它严格依赖于它的一个派生类,不是一个好的设计理念。也许你应该重新考虑设计并放弃抽象矩阵类的想法,但只让它成为一个类模板。

像这样使用的抽象接口不适合值语义计算。

不能返回抽象类的实例。 实际实例(值)具有固定的存储大小和类型(以C++为单位)。 派生抽象接口的实例不会"适合"。

有几种方法可以解决这个问题。 一种方法是实现自己的多态性。

从像你一样template<class T>class AbstMatrix开始。 摆脱operator-公开纯虚拟方法。 虚拟操作员"工作",但使用起来很尴尬,所以不要打扰。 包括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等。 它的工作是成为多态的价值语义类型,因为它的行为由它在唯一 ptr 中拥有的抽象类决定。

这使您具有多态值类型。

现在,您的实现继承自AbstMatrix<T>,并且可以存储在Matrix<T>中。

最新更新