继承函数返回派生类,而不是基类

use*_*923 5 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;
}
Run Code Online (Sandbox Code Playgroud)

动机

在实际例子中,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
Run Code Online (Sandbox Code Playgroud)

由于operator*()存在 3 维和 2 维向量等的派生类,因此覆盖所有派生类的工作量增加了。

我知道一个解决方案是定义一个从Matrixto Vector(and to Vector3, Vector2, ...) 的强制转换,但这将涉及复制所有条目(为了效率,堆栈数组)。

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

  1. 复制每个派生类中的所有相关代码,或者
  2. 定义演员表?

在我目前的理解中,相互矛盾的问题是:

  1. 重复的代码使解决方案容易出错并且更难重构。
  2. 每次“范围”在 Matrix、Vector、Vector3 等之间发生变化时,重用现有代码都需要进行大量复制操作。如果用于大型计算,效率会很低。

任何建议将不胜感激。谢谢!

Yak*_*ont 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;
}
Run Code Online (Sandbox Code Playgroud)

现在如果我做得对的话

(d1+d2).timesTwo();
Run Code Online (Sandbox Code Playgroud)

作品。

我也+按照实施,+=因为通常效果很好。

奇特的启用 if 存在,因为当您传递Base和从模板类型派生的类型并继续在结果类型上Base使用时,使用非常通用的模板运算符的 koenig 查找会导致奇怪的事情发生。+通过说“只有源自Base”的事情,正确的事情就会发生。

*this我们需要使用一个无模板的友元函数,这样我们就可以获取模板中“”的类型(因为它在哪里)来更改我们的返回类型。这不能在模板成员函数中完成。

enable_if子句在 MSVC 中效果不佳,但在其他编译器中是最佳实践。对于 MSVC,请使用class=enable_if而不是enable_if=0. 为什么=0最好的原因超出了这里的范围。