C++ 在模板派生类中用协变返回类型重写虚函数
我想重写模板派生类中的虚拟函数。但是,我想使用派生类作为返回类型。以下是相应的代码:C++ 在模板派生类中用协变返回类型重写虚函数,c++,inheritance,types,C++,Inheritance,Types,我想重写模板派生类中的虚拟函数。但是,我想使用派生类作为返回类型。以下是相应的代码: class Abstract { public: virtual Abstract* allocate() const = 0; }; template <typename Derived> class Base : public Abstract { public: Derived* allocate() const override { return new
class Abstract {
public:
virtual Abstract* allocate() const = 0;
};
template <typename Derived>
class Base : public Abstract {
public:
Derived* allocate() const override {
return new Derived;
}
};
class Concrete : public Base<Concrete> {
public:
};
int main() {
Concrete c;
delete c.allocate();
}
将
allocate
函数移到Concrete
类中可以解决这个问题,但在创建多个具体类时会导致代码重复。有没有办法让编译器知道派生的
实际上是从抽象的
派生的?您可以模拟协变返回类型:
这里的要点是类重新引入
。没有它,allocate
将覆盖虚拟函数。使用它,它隐藏了继承的函数
#include <iostream>
class Abstract {
public:
virtual Abstract* allocate() const = 0;
};
namespace details{
class Reintroduce {};
template <typename Derived>
class AllocImpl : public Abstract
{
public:
Abstract* allocate() const override {
return new Derived;
}
};
}
template <typename Derived>
class Base : public details::AllocImpl<Derived>
{
public:
Derived* allocate(details::Reintroduce = {}) const
{
return static_cast<Derived*>(details::AllocImpl<Derived>::allocate());
}
};
class Concrete : public Base<Concrete> {
public:
};
int main() {
Concrete c;
delete c.allocate();
}
#包括
类摘要{
公众:
虚拟抽象*allocate()常量=0;
};
命名空间详细信息{
类重新引入{};
模板
类AllocImpl:公共摘要
{
公众:
抽象*分配()常量重写{
返回新导出的;
}
};
}
模板
类库:公共详细信息::AllocImpl
{
公众:
派生*分配(详细信息::重新引入={})常量
{
返回静态_cast(details::AllocImpl::allocate());
}
};
混凝土类别:公共基础{
公众:
};
int main(){
混凝土c;
删除c.allocate();
}
如果我们能够在没有
AllocImpl
的情况下实现它,那将是一件好事,但这将使调用变得模棱两可。问题是具体的
在基础中不完整(无论是基础还是)(与任何CRTP一样)
所以编译器还不知道Concrete
继承自Abstract
可能的解决方案是不使用CRTP,而是使用常规继承:
template <typename Base>
class Derived : public Base {
public:
Derived* allocate() const override {
return new Derived;
}
};
class ConcreteImpl : public Abstract {
public:
};
using Concrete = Derived<ConcreteImpl>;
模板
派生类:公共基{
公众:
派生的*分配()常量重写{
返回新导出的;
}
};
类ConcreteImpl:公共摘要{
公众:
};
使用混凝土=衍生材料;
修改引入类型标记以区分不同的分配
函数,我们可以创建一个更简单的解决方案。诀窍在于,我们在Abstract
类中引入type标记,并重写allocate
方法,而不提供默认参数(以避免歧义)。以下解决方案的缺点是,我们需要能够更改抽象
类中分配
方法的签名,这可能并不总是可行的
class BaseTag {};
class Abstract {
public:
virtual Abstract* allocate(BaseTag = {}) const = 0;
};
template <typename Derived>
class Base : public Abstract {
public:
Abstract* allocate(BaseTag) const override { return allocate(); }
Derived* allocate() const {
return new Derived;
}
};
class Concrete : public Base<Concrete> {
public:
};
int main() {
Concrete c;
delete c.allocate();
Abstract* a = &c;
delete a->allocate();
}
classbasetag{};
类摘要{
公众:
虚拟抽象*分配(BaseTag={})常量=0;
};
模板
类库:公共摘要{
公众:
抽象*分配(BaseTag)常量重写{return allocate();}
派生的*分配()常量{
返回新导出的;
}
};
混凝土类别:公共基础{
公众:
};
int main(){
混凝土c;
删除c.allocate();
摘要*a=&c;
删除->分配();
}
这也是个好主意。这很好,因为这样你就不需要演员阵容了。但是,如果您想返回封装在“智能指针”中的协变返回类型指针,该怎么办?不幸的是,智能指针不是协变的:-/您可能仍然有两种方法:返回指针的协变方法,一个非虚拟类,它调用虚拟类将结果封装在智能指针中。在您的示例中,您使用的是“中介”类,与“CRTP”相反。好主意。
class BaseTag {};
class Abstract {
public:
virtual Abstract* allocate(BaseTag = {}) const = 0;
};
template <typename Derived>
class Base : public Abstract {
public:
Abstract* allocate(BaseTag) const override { return allocate(); }
Derived* allocate() const {
return new Derived;
}
};
class Concrete : public Base<Concrete> {
public:
};
int main() {
Concrete c;
delete c.allocate();
Abstract* a = &c;
delete a->allocate();
}