【问题标题】:Deriving from a class with operator overloading从具有运算符重载的类派生
【发布时间】:2009-04-14 04:57:08
【问题描述】:

我想创建一个行为类似于数学向量的类的集合,以便将对象乘以标量将每个字段乘以该数量等。问题是我希望字段具有实际名称,而不是作为索引处理。

我最初的想法是创建一个具有重载的基类 Rn,然后创建具有漂亮名称的派生类。像这样的:

#include <iostream>
#include <algorithm>
using namespace std;

template<int N, class X=double>
struct Base{
    X xs[N];

    Base(){};

    Base(X *data){
        copy(data, data+N, xs);
    }

    Base operator*= (double d){
        for(int i=0; i<N; i++){
            xs[i] *= d;
        }
        return *this;
    }

    Base operator* (double d){
        Base answer = *this;
        answer *= d;
        return answer;
    }

    //also operators for +=, +, multiplication from left, maybe [] too
};

struct Derived : public Base<2>{
    Derived(double a, double b){
        foo() = a;
        bar() = b;
    }

    double &foo(){ return xs[0]; }
    double &bar(){ return xs[1]; }
};

int main()
{
    //this is OK:
    double data[2] = {0.0, 2.0};
    Base<2> b(data);
    b = b*17.0;

    cout << b.xs[0] << endl;

    //I can't do this:
    Derived x(0.0, 2.0);
    x = x*17.0;

    cout << x.foo() << endl;

    return 0;
}

每当我尝试使用需要复制的运算符时,都会出现编译器错误。 gcc 给了我以下编译器错误:

teste.cpp: In function ‘int main()’:
teste.cpp:52: error: no match for ‘operator=’ in ‘x = x.Derived::<anonymous>.Base<N, X>::operator* [with int N = 2, X = double](1.7e+1)’
teste.cpp:31: note: candidates are: Derived& Derived::operator=(const Derived&)

我认为问题在于重载函数处理的 Base 对象无法转换为 Derived 对象,因此我无法在派生类中使用它们。但是,我想不出解决方案。有没有办法解决这个问题,还是我应该使用完全不同的方法?

额外问题:有什么方法可以使用 std::valarray 来避免输入大量的运算符重载?

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    您的 Base 运算符(在本例中为 *)可以接受 Derived 对象,但它们返回一个 Base,它不能用作 Derived 默认赋值运算符中的右手操作数。解决此问题的最简单方法是向 Derive 添加一个赋值运算符,该运算符将采用 Base:

    Derived& operator= (const Base<2>& other)
    

    您必须将它添加到任何派生类中,但实现相当简单(您可以在 Base 中有一个 void CopyOtherBase 函数来执行复制,并让所有 operator= 调用它并返回 *this)。

    【讨论】:

    • 这应该返回一个 Derived&(特别是 *this)。它不允许高效实现 operatorX=,因为我相信您需要提供完整的外观。
    • 你是对的,当然。感谢您的更正,我修正了我的答案。
    【解决方案2】:

    我只会解决技术上的困难,而不是这是否是一个好主意。

    问题是 Derived 的 operator* 的结果是一个 Base,而 Derived 的 operator=(这是一个默认的 operator=)不知道如何“吃掉”一个 Base。

    一个简单的解决方案是创建一个 Derived 的构造函数,该构造函数获取一个 Base,并执行正确初始化自身所需的任何操作。这将允许将 Base 即时转换为 Derived - 并且适用于所有其他需要 Base 的 Derived 运算符。

    类似于-

    Derived(const Base<2>& B) : Base<2>( B )
    {
    }
    

    【讨论】:

    • memcpy 通常是个坏主意。如果 base 不是 POD,则代码可能会严重损坏。喜欢更惯用的: Derived( const Base& b ): Base(b) {}
    【解决方案3】:

    我认为使用枚举或静态常量来命名字段会更好

    例如,静态常量 int FIELD_NAME = 0;
    静态 const int FIELD_NAME2 = 1;

    而不是将其作为不同的类型(模板)。然后,您可以使用 std::valarray 或 boost::ublas::vector/matrix 类型来利用现有代码并获得高质量的性能向量操作来启动。

    【讨论】:

      【解决方案4】:

      模板元编程有一个有趣的想法,可以解决数学向量的此类问题,但可能无法解决您命名零件的问题。

      David Abrahams,Aleksey Gurtovoy:C++ 模板元编程:Boost 及其他领域的概念、工具和技术,Addison-Wesley,ISBN 0-321-22725-5

      【讨论】:

        【解决方案5】:

        According to MSDN,

        所有重载的运算符,除了 赋值 (operator=) 被继承 按派生类。

        这是你的问题吗?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-06
          • 2020-04-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多