【问题标题】:How to return a templated derived class instead of base class in an overridden function?如何在重写函数中返回模板派生类而不是基类?
【发布时间】:2020-04-17 14:50:05
【问题描述】:
#include <iostream>
template <typename T>
class A {};
template <typename T>
class B : public A<T> {};
class C
{
virtual A<int> func() = 0;
};
class D : public C
{
virtual B<int> func() override
{
return B<int>{};
}
};
这里B 继承自A,模板参数是int 用于两个返回值。为什么我不能只返回B<int> 而不是A<int>?
【问题讨论】:
标签:
c++
templates
inheritance
return-type
【解决方案1】:
正如 merlinND 所指出的,您必须从 class A<T> 继承。但它仍然会失败,除非您返回一个指针或对 B 的引用而不是值类型。以下将编译。
#include <iostream>
template <typename T>
class A {};
template <typename T>
class B : public A<T> {};
class C
{
virtual A<int>* func() = 0;
};
class D : public C
{
virtual B<int>* func() override
{
return new B<int>{};
}
};
【解决方案2】:
当声明 B 时,你必须继承一个实际的类,而不是一个模板:
template <typename T>
class B : public A<T> {};
第二个问题是在类D 中覆盖func() 的签名时不能更改它。但是,一旦解决此问题,您将能够按预期返回 B<int>{}。
编辑:@Josh Wilson 指出的第三个问题是,您不会从 func() 的返回值中获得 B 的行为,因为它指定了按值返回的 A。您需要返回一个指向 A<int> 的指针以应用多态性。
【解决方案3】:
B D::func() 不能覆盖 AC::func(),因为这两个函数的返回类型彼此不同。
【解决方案4】:
来自cppreference:
如果函数 Derived::f 覆盖函数 Base::f,则它们的返回类型必须相同或协变。
两种类型协变需要满足三个条件,其中之一是它们需要是指针类型或引用类型。
A<int> 和 B<int> 既不是同一类型也不是协变类型,这意味着您不能使用 B<int> 作为被覆盖方法的返回类型。
还请注意,您不必在被覆盖的方法中使用 virtual 说明符。
此外,正如其他人所注意到的,以下将起作用并返回一个A<int>-typed 对象:
A<int> func() override
{
return B<int>{};
}
但你应该避免它。事实上,在创建B<int> 对象后,它会切片 成A<int> 类型,原因我们已经看到,这意味着您的对象类型实际上只是A<int>,并且不是B<int> 的实例。这意味着当您本可以创建 A<int> 时,您徒劳地创建了 B<int> 实例。
如果您需要B<int>,则需要返回指针或引用。