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

Inherited functions to return Derived class, not Base class

本文关键字:基类 函数 返回 派生 继承      更新时间:2023-10-16

在C++中,是否可以在基类中公式化返回基类的函数,以便在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

由于存在3维和2维向量等的派生类,增加了覆盖所有派生类的努力,例如operator*()

我知道解决方案是定义从MatrixVector(以及到Vector3Vector2…)的强制转换,但这将涉及复制所有条目(为了提高效率,这些条目是堆栈数组)。

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

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

在我目前的理解中,相互冲突的问题是:

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

如有任何建议,我们将不胜感激。谢谢

是的,但仅限自由函数(包括大多数运算符)。

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();

工作。

我还根据+=实现了+,因为它通常工作得很好。

花式启用if是因为当您将Base和从Base派生的类型传递给模板类型并继续对生成的类型使用+时,使用非常通用的模板运算符的koenig查找会导致奇怪的事情发生。只要说"只有源自Base的东西",正确的事情就会发生。

我们需要使用一个无模板的朋友函数,这样我们就可以在模板中获得"*this"的类型(如它所在的位置)来更改我们的返回类型。这不能在模板成员函数中完成。

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

使class Base抽象,并将其方法放在分离的函数中。还声明纯虚拟派生类中所需的方法:

#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;
}