在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维向量的派生等而增加。
我理解解决方案是定义从Matrix
到Vector
(以及Vector3
,Vector2
,...)的演员表,但这会涉及全部复制条目(为了效率,堆栈数组)。
是否有更有效的解决方案?而且,如果没有,通常会被认为更清洁/更好
在我目前的理解中,矛盾的问题是:
任何建议都会受到最高的赞赏。谢谢!
答案 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;
}