我有一个“接口”作为.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*)->Implementor::parent”)从“std::shared_ptr”转换为“std::shared_ptr”
因此,std::shared_ptr基本上是一个包装器,编译器不知道如何从wrapper<apple>到wrapper<fruit>,即使苹果扩展了水果。
我怎样才能规避这种行为呢?
编辑:看起来在c++中仍然是不可能的,因为协变类型只适用于指针/引用,而不是像std::shared_ptr这样的包装器.遗憾的是:
发布于 2017-03-30 19:49:14
协变量返回类型只适用于原始指针和引用,因为编译器知道它们是如何工作的。要使它适用于任意类型,编译器将需要被告知“这对于协变量返回类型是安全的”,就像C#对out T泛型参数所做的那样,但是C++中没有这样的特性。
不幸的是,您需要在std::shared_ptr<Parent>中返回Implementor。
发布于 2017-03-30 19:57:50
您可以满足两个虚拟接口,并通过两个成员函数向那些访问派生类的人提供附加信息:
struct Implementor : ISomeInterface
{
shared_ptr<Parent> getX() override { return getX_fromImplementor(); }
shared_ptr<Child> getX_fromImplementor() // not virtual!
{
// real implementation here
}
};发布于 2017-03-30 20:00:50
这是可能的。
现在,天真地说,这在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); }。
实例化。
你也许可以用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上,这样就没有任何意义了。如果你在你的侧写的建筑里发现了它,请告诉我。;)
https://stackoverflow.com/questions/43127045
复制相似问题