首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >std::shared_ptr<Parent>到std::shared_ptr<Child>

std::shared_ptr<Parent>到std::shared_ptr<Child>
EN

Stack Overflow用户
提问于 2017-03-30 19:45:01
回答 3查看 1.9K关注 0票数 2

我有一个“接口”作为.h文件,该文件具有如下所示的虚拟方法:

代码语言:javascript
复制
class ISomeInterface {
    public:
        virtual std::shared_ptr<Parent> getX() = 0;
}

现在,父类是“抽象的”,在接口的实现者中,我使用了一个实际的类。所以我想这么做:

代码语言:javascript
复制
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这样的包装器.遗憾的是:

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-03-30 19:49:14

协变量返回类型只适用于原始指针和引用,因为编译器知道它们是如何工作的。要使它适用于任意类型,编译器将需要被告知“这对于协变量返回类型是安全的”,就像C#对out T泛型参数所做的那样,但是C++中没有这样的特性。

不幸的是,您需要在std::shared_ptr<Parent>中返回Implementor

票数 4
EN

Stack Overflow用户

发布于 2017-03-30 19:57:50

您可以满足两个虚拟接口,并通过两个成员函数向那些访问派生类的人提供附加信息:

代码语言:javascript
复制
struct Implementor : ISomeInterface
{
    shared_ptr<Parent> getX() override { return getX_fromImplementor(); }

    shared_ptr<Child> getX_fromImplementor()  // not virtual!
    {
        // real implementation here
    }
};
票数 1
EN

Stack Overflow用户

发布于 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>的行为;他们别无选择,编译器强制执行。

代码语言:javascript
复制
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稍微简化一下,但是..。这会更难理解。

代码语言:javascript
复制
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看起来像:

代码语言:javascript
复制
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上,这样就没有任何意义了。如果你在你的侧写的建筑里发现了它,请告诉我。;)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43127045

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档