[英]std::shared_ptr<Parent> to std::shared_ptr<Child>
我有一个“接口”作为.h文件,有一个像这样的虚拟方法:
class ISomeInterface {
public:
virtual std::shared_ptr<Parent> getX() = 0;
}
既然父是“抽象”的,在接口的实现者中我使用的是实际的类。 所以我想这样做:
class Implementor : public ISomeInterface {
public:
std::shared_ptr<Child> getX() = { return this->x; }
}
但后来我得到:
无法将'((Implementor *)this' - > Implementor :: parent'从'std :: shared_ptr'转换为'std :: shared_ptr'
所以,基本上std :: shared_ptr是一个包装器,编译器不知道如何从wrapper<apple>
到wrapper<fruit>
,即使apple扩展了果实。
我该如何规避这种行为?
编辑:在c ++中看起来这仍然是不可能的,因为协变类型只适用于指针/引用,而不是像std :: shared_ptr这样的包装器...一个耻辱:(
你不能。 协变返回类型仅适用于原始指针和引用,因为编译器知道它们是如何工作的。 为了使其适用于任意类型,编译器需要能够被告知“使用协变返回类型是安全的”,就像C#没有out T
通用参数那样,但C ++中没有这样的特性。
不幸的是,您需要在Implementor
返回std::shared_ptr<Parent>
。
您可以通过具有两个成员函数来满足虚拟接口并向有权访问派生类的人员提供其他信息:
struct Implementor : ISomeInterface
{
shared_ptr<Parent> getX() override { return getX_fromImplementor(); }
shared_ptr<Child> getX_fromImplementor() // not virtual!
{
// real implementation here
}
};
这个有可能。
现在,天真地,这在C ++中是不允许的。 C ++中的协变返回类型不能以这种方式工作。
但这是C ++。 C ++不会因为语言不支持某项功能而停止并放弃。 我们可以自己编写这个功能。
诀窍是你实际上并不关心getX
是否真的是虚拟的; 你只是希望它有虚拟行为。
我们创建了一系列非虚拟getX
其中包含一系列调度的getX_impl
虚函数。 我们使用final
和基于指针的调度来抵抗小错别字。
从用户方面来看,它就像协变返回类型。 实现明智,你必须编写两个简短的样板文件。
这种设计的一个重要部分是它不会发生不安全的演员表 。 一个简单的捷径就是取消final
和新的getX_impl
方法; 成本是我们不能保证进一步派生的孩子实际上会放置一个shared_ptr<Child>
。
final
父级将调度到公共接口。 公共接口将调度到一个新的虚函数,该函数重新调整shared_ptr<Child>
。 想要改变行为的孩子必须覆盖返回shared_ptr<Child>
; 他们没有选择,编译器强制执行它。
class ISomeInterface {
virtual std::shared_ptr<Parent> getX_impl(ISomeInterface *) = 0;
public:
std::shared_ptr<Parent> getX() { return getX_impl(this); }
};
class Implementor : public ISomeInterface {
std::shared_ptr<Child> x = std::make_shared<Child>();
virtual std::shared_ptr<Parent> getX_impl(ISomeInterface*) final override { return getX(); }
virtual std::shared_ptr<Child> getX_impl(Implementor*) { return this->x; }
public:
std::shared_ptr<Child> getX() { return getX_impl(this); }
};
用户只需调用getX()
。 它的行为几乎就像一个具有协变共享指针返回类型的虚方法。 它甚至可以与成员函数指针一起使用!
使用新类型实现的每个类都会最终确定父方法,创建一个新的私有虚拟getX_impl
方法,返回新类型,具有新的父调用getX()
,并公开一个公共getX() { return getX_impl(this); }
getX() { return getX_impl(this); }
,其分派到正确的过载。
实例 。
你可以用CRTP略微简化这个,但是......它会更难理解。
template<class D, class Child, class B>
struct getX_CRTP : B {
std::shared_ptr<Child> getX() { return static_cast<D*>(this)->getX_impl(static_cast<D*>(this)); }
private:
virtual decltype( std::declval<B&>().getX() ) getX_impl( B* ) final override { return getX(); }
virtual std::shared_ptr<Child> getX_impl( D* ) = 0;
};
现在, Implementor
看起来像:
class Implementor : public getX_CRTP<Implementor, Cihld, ISomeInterface>
{
std::shared_ptr<Child> x = std::make_shared<Child>();
virtual std::shared_ptr<Child> getX_impl(Implementor*) override { return this->x; }
};
每个派生类更短,但不是更清楚。
我们也可以用某种标签替换指向自己类型的指针,将无意义地推送到堆栈上的字节从sizeof(ptr)
到1
,并且保持未初始化。 如果您在配置的构建中发现它,请告诉我。 ;)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.