如何在抽象基类中实现operator +

时间:2017-06-10 22:20:33

标签: c++ templates operator-overloading abstract-class

我想实现一个抽象的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!

3 个答案:

答案 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>中。