使用 C++ 定义接口(在不丢失基类的所有重载运算符的情况下派生)

Posted

技术标签:

【中文标题】使用 C++ 定义接口(在不丢失基类的所有重载运算符的情况下派生)【英文标题】:Interface definition with C++ (deriving without losing all the overloaded operators from base) 【发布时间】:2015-09-22 13:13:17 【问题描述】:

我想用 Eigen 库替换库的一部分。 因为有很多依赖,我想写一个基于 Eigen 的接口。

下面的这个接口使用 cxx11 typedefs 和重载来为 3D 矢量重新创建旧的类接口。这种方法的问题在于,我似乎必须重新实现 Eigen 中已经存在的所有运算符函数/构造函数(+/-/*/..)。

下面的例子说明了这个问题。如果我将 Eigen 的重载运算符与派生类一起使用,则会出现编译器错误。基类按预期工作正常。有没有办法以更少的努力将所需的接口函数/成员添加到 Eigen 类?例如。通过自动将派生类转换为基类?我完全不想重新实现所有 Eigen 函数:D

#include <iostream>
using namespace std;
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Dense>


namespace InterfDef 
    template <class T>
    using Vector3 = Eigen::Matrix<T, 3, 1>;
;

namespace InterfImp 
    template <class T>
    class Vector3 : public InterfDef::Vector3<T> 
    public:
        // This is all what I wanted to add :)
        T &x = (*this)[0];
        T &y = (*this)[1];
        T &z = (*this)[2];

        // All this crap below, I don't want to implement :(
        Vector3(InterfDef::Vector3<T> v) :
            InterfDef::Vector3<T>(v) 

        

        Vector3(T x, T y, T z) :
            InterfDef::Vector3<T>(x,y,z) 

        

        Vector3& operator=(const InterfDef::Vector3<T>& v) 
            (*this)[0] = v[0];
            (*this)[1] = v[2];
            (*this)[2] = v[2];
            return *this;
        
    ;
;

问题来了。调用标量乘法运算符仅适用于非派生类,因为向量类的转换在派生后失败。

int main() 
    InterfDef::Vector3<float> a(1,2,3);
    InterfImp::Vector3<float> b(2,3,4);
    InterfImp::Vector3<float> c(a);
    InterfImp::Vector3<float> d = b;

    cout << "Hello World!" << c.x << "; " << c.y << "; " << c.z << std::endl;
    cout << "Hello World!" << d.x << "; " << d.y << "; " << d.z << std::endl;

    InterfImp::Vector3<float> e = 2 * d; // error with my derived class 
    InterfDef::Vector3<float> e = 2 * d; // works

    cout << "Hello World!" << e[0] << "; " << e[1] << "; " << e[2] << std::endl;

    return 0;

【问题讨论】:

【参考方案1】:

第一件事

您的外壳使用矩阵 x()、y()、z() 函数。如果矩阵不是 const,它们会返回对其标量的 reference,这会使您的代码如下所示:

Vector3d a;
a.x() = 1;// Ok, a.x() is l-value
a.y() = 2;// ""
a.y() = 3;// ""

注意这是无效的,因为矩阵是常数:

const Vector3d b;
b.x() = 1;// Error! b.x() is r-value

还有其他需要...

Eigen 在Customizing/Extending Eigen page 中准确地记录了您想要做什么,甚至您尝试这样做的方式。

我将只是重复那里所说的话:

第一个解决方案 - 转换构造函数

你总是可以使用带有模板的转换构造函数:

InterfImp 
    template <class T>
    class Vector3 : public InterfDef::Vector3<T> 
        //...
        // This constructor allows you to construct Vector3 from Eigen expressions
        template<typename OtherDerived>
        Vector3(const Eigen::MatrixBase<OtherDerived>& other)
            : InterfDef::Vector3::Vector3d(other)
         

        // This method allows you to assign Eigen expressions to Vector3
        template<typename OtherDerived>
        Vector3& operator= (const Eigen::MatrixBase <OtherDerived>& other)
        
            this->Base::operator=(other);
            return *this;
        
    ;

这样,即使知道某些函数返回矩阵表达式(MatrixBase),你也可以转换它们。

注意你应该如何实现 operator=。

第二种解决方案 - Eigen 插件宏

Eigen 允许您自定义它的类,因为它是一个只有头文件的库。 在包含 Eigen 之前,将 EIGEN_MATRIXBASE_PLUGIN 定义为要注入 MarixBase 类的标头:

#define EIGEN_MATRIXBASE_PLUGIN my_matrixbase_injection.h
// Now safely include Eigen headers

在 my_matrixbase_injection.h 中:

    // Add what you want to add here.
    void superCoolFunc() const
    
        std::cout << "Super COOL!!!\n";
    

【讨论】:

以上是关于使用 C++ 定义接口(在不丢失基类的所有重载运算符的情况下派生)的主要内容,如果未能解决你的问题,请参考以下文章

在C++中,啥是运算符重载?啥是虚函数?

c++实验四 类的继承派生和多态

c ++:是不是可以在派生类中定义重载运算符,这将工作objs基类类型

C++ string类的模拟实现

C++ string类的模拟实现

实验4 类的继承派生和多态