返回Derived类的继承函数,而不是Base类

时间:2017-01-14 16:06:43

标签: c++ inheritance

在C ++中是否可以在返回Base类型的Base类中制定函数,以便在Derived类中返回Derived类型而不会重载?

最小例子:

class Base
{
    public:
        Base(double v)
        {
            value = v;
        }

        Base add(Base b)
        {
            return Base(b.value + this->value);
        }

        void print()
        {
            std::cout << value << std::endl;
        }

        double value;
};



class Derived : public Base
{
    public:
        Derived(double v) : Base(v)
        {

        }

        void timesTwo()
        {
            value *= 2.0;
        }
};


int main()
{

    Derived d1(1), d2(2);

    // This doesn't work because the result is of type Base
    (d1.add(d2)).timesTwo();


    return 0;
}

动机

在实际示例中,Base表示线性代数矩阵,Derived表示向量。矩阵提供了许多适用于向量的函数,例如标量的加法或乘法。

在这种情况下,不希望手动覆盖所有这些矩阵函数以返回向量。如果可能的话,我想表示无论this类型是什么,返回类型都应该与它相同。

示例:

class Matrix
{
    ...
    Matrix operator*(double x);
};

class Vector : Matrix
{
    ...
};

Matrix M;
M = M * 2.0; // works

Vector v;
v = v * 2.0; // does not work, because v * 2.0 returns a Matrix

重写的努力,例如所有派生类的operator*()因为存在3维和2维向量的派生等而增加。

我理解解决方案是定义从MatrixVector(以及Vector3Vector2,...)的演员表,但这会涉及全部复制条目(为了效率,堆栈数组)。

是否有更有效的解决方案?而且,如果没有,通常会被认为更清洁/更好

  1. 复制每个派生类中的所有相关代码,或复制到
  2. 定义一个演员?
  3. 在我目前的理解中,矛盾的问题是:

    1. 重复的代码使解决方案容易出错并且更难以重构。
    2. 每次“范围”在Matrix,Vector,Vector3,...之间发生变化时,重复使用现有代码都需要大量复制操作。如果在大型计算中使用,效率会很低。
    3. 任何建议都会受到最高的赞赏。谢谢!

2 个答案:

答案 0 :(得分:4)

是的,但仅限于免费功能(包括大多数操作员)。

template<class X, class Y,
  std::enable_if_t<std::is_base_of<Base, std::decay_t<X>>{},int> =0,
  std::enable_if_t<std::is_base_of<Base, std::decay_t<Y>>{},int> =0
>
friend X& operator+=(X&x, Y&& rhs)
{
  x.value += rhs.value;
  return x.
}
template<class X, class Y,
  std::enable_if_t<std::is_base_of<Base, std::decay_t<X>>{},int> =0,
  std::enable_if_t<std::is_base_of<Base, std::decay_t<Y>>{},int> =0
>
friend std::decay_t<X> operator+(X&&x, Y&& rhs) {
  auto r=std::forward<X>(x);
  r+=std::forward<Y>(rhs);
  return r;
}

现在,如果我做得对,

(d1+d2).timesTwo();

作品。

我还在+方面实施了+=,因为这通常效果很好。

如果存在花哨的启用,因为当您将Base和从Base派生的类型传递给模板类型并继续使用+时,使用非常通用的模板运算符进行koenig查找会导致奇怪的事情发生在结果类型上。通过说“只有来自Base的东西”,正确的事情发生了。

我们需要使用模板免费的朋友函数,这样我们就可以在模板中获得“*this”的类型(就像它在哪里)来改变我们的返回类型。这不能在模板成员函数中完成。

enable_if子句在MSVC中不能很好地工作,但在其他编译器中是最佳实践。对于MSVC,请使用class=enable_if代替enable_if=0=0最好的原因超出了范围。

答案 1 :(得分:0)

创建类Base抽象并将其方法放在单独的函数中。还要在派生类中声明pure virtual所需的方法:

#include <iostream>

class Base
{
public:
    Base(double v) : value(v) {}

    double value;
    virtual void timesTwo() = 0;
};

class Derived : public Base
{
public:
    Derived(double v) : Base(v) {}

    void timesTwo()
    {
        value *= 2.0;
        std::cout << "timesTwo " << value << std::endl;
    }
};

template <class T>
T add(const T& t1, const T& t2)
{
    return T(t1.value + t2.value);
}

int main()
{
    Derived d1(1), d2(2);
    add(d1, d2).timesTwo();

    return 0;
}