std :: shared_ptr <parent>到std :: shared_ptr <child>

时间:2017-03-30 19:45:02

标签: c++ c++14

我有一个“接口”作为.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' - &gt; Implementor :: parent'从'std :: shared_ptr'转换为'std :: shared_ptr'

所以,基本上std :: shared_ptr是一个包装器,编译器不知道如何从wrapper<apple>传递到wrapper<fruit>,即使apple扩展了果实。

我如何规避这种行为?

编辑:在c ++中看起来仍然无法实现这一点,因为协变类型只适用于指针/引用,而不是像std :: shared_ptr这样的包装器...耻辱:(

3 个答案:

答案 0 :(得分:5)

你做不到。协变返回类型仅适用于原始指针和引用,因为编译器知道它们是如何工作的。为了使其适用于任意类型,编译器需要能够被告知“使用协变返回类型是安全的”,就像C#与out T泛型参数一样,但C ++中没有这样的特性。 / p>

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

答案 1 :(得分:2)

您可以同时满足虚拟接口,并通过两个成员函数为有权访问派生类的人提供其他信息:

struct Implementor : ISomeInterface
{
    shared_ptr<Parent> getX() override { return getX_fromImplementor(); }

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

答案 2 :(得分:1)

这是可能的。

现在,天真地,这在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(),并公开公共{{1调度到正确的重载。

Live example

你可以用CRTP略微简化这一点,但是......它会更难理解。

getX() { return getX_impl(this); }

现在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)无意义地推到堆栈上的字节数减少,并保留未初始化的字节。如果您在配置的构建中发现它,请告诉我。 ;)