如何使用智能指针协变返回types?

我有这样的代码:

class RetInterface {...} class Ret1: public RetInterface {...} class AInterface { public: virtual boost::shared_ptr<RetInterface> get_r() const = 0; ... }; class A1: public AInterface { public: boost::shared_ptr<Ret1> get_r() const {...} ... }; 

这段代码不能编译。

在视觉工作室,它引发了

C2555:重写虚函数返回types不同,并且不是协变的

如果我不使用boost::shared_ptr但返回原始指针,代码编译(我明白这是由于在C ++ 协变返回types )。 我可以看到问题是因为Ret1 boost::shared_ptr不是从RetInterface boost::shared_ptr RetInterface 。 但是我想返回Ret1 boost::shared_ptr以供其他类使用,否则我必须在返回后Ret1返回值。

  1. 难道我做错了什么?
  2. 如果没有,为什么这样的语言 – 在这种情况下,应该可以扩展处理智能指针之间的转换? 有没有一个理想的解决方法?

首先,这确实如何在C ++中工作:派生类中的虚函数的返回types必须与基类中的相同。 还有一个特殊的例外是返回一个引用/指向某个类X的函数可以被一个返回引用X的类的引用/指针的函数覆盖,但是正如你注意到的那样,这不允许使用智能指针(如shared_ptr ),只是为了简单的指针。

如果您的界面RetInterface足够全面,那么您将不需要知道调用代码中实际返回的types。 总的来说,这是没有意义的: get_r是一个virtual函数,首先是因为你将通过一个指针或引用基类AInterface来调用它,在这种情况下,你不知道什么types派生类将返回。 如果你用一个实际的A1引用来调用它,你可以在A1中创build一个独立的get_r1函数来完成你所需要的。

 class A1: public AInterface { public: boost::shared_ptr<RetInterface> get_r() const { return get_r1(); } boost::shared_ptr<Ret1> get_r1() const {...} ... }; 

或者,您可以使用访问者模式或类似于我的Dynamic Double Dispatch技术将callback传递给返回的对象,然后可以使用正确的types调用callback。

那么这个解决scheme呢?

 template<typename Derived, typename Base> class SharedCovariant : public shared_ptr<Base> { public: typedef Base BaseOf; SharedCovariant(shared_ptr<Base> & container) : shared_ptr<Base>(container) { } shared_ptr<Derived> operator ->() { return boost::dynamic_pointer_cast<Derived>(*this); } }; 

例如:

 struct A {}; struct B : A {}; struct Test { shared_ptr<A> get() {return a_; } shared_ptr<A> a_; }; typedef SharedCovariant<B,A> SharedBFromA; struct TestDerived : Test { SharedBFromA get() { return a_; } }; 

在C ++中重载方法时,您不能更改返回types(对于非指针,非引用返回types)。 A1::get_r必须返回一个boost::shared_ptr<RetInterface>

安东尼威廉姆斯有一个很好的综

这是我的尝试:

 template<class T> class Child : public T { public: typedef T Parent; }; template<typename _T> class has_parent { private: typedef char One; typedef struct { char array[2]; } Two; template<typename _C> static One test(typename _C::Parent *); template<typename _C> static Two test(...); public: enum { value = (sizeof(test<_T>(nullptr)) == sizeof(One)) }; }; class A { public : virtual void print() = 0; }; class B : public Child<A> { public: void print() override { printf("toto \n"); } }; template<class T, bool hasParent = has_parent<T>::value> class ICovariantSharedPtr; template<class T> class ICovariantSharedPtr<T, true> : public ICovariantSharedPtr<typename T::Parent> { public: T * get() override = 0; }; template<class T> class ICovariantSharedPtr<T, false> { public: virtual T * get() = 0; }; template<class T> class CovariantSharedPtr : public ICovariantSharedPtr<T> { public: CovariantSharedPtr(){} CovariantSharedPtr(std::shared_ptr<T> a_ptr) : m_ptr(std::move(a_ptr)){} T * get() final { return m_ptr.get(); } private: std::shared_ptr<T> m_ptr; }; 

还有一个例子:

 class UseA { public: virtual ICovariantSharedPtr<A> & GetPtr() = 0; }; class UseB : public UseA { public: CovariantSharedPtr<B> & GetPtr() final { return m_ptrB; } private: CovariantSharedPtr<B> m_ptrB = std::make_shared<B>(); }; int _tmain(int argc, _TCHAR* argv[]) { UseB b; UseA & a = b; a.GetPtr().get()->print(); } 

解释:

这个解决scheme意味着meta-progamming和修改covariant智能指针中使用的类。

简单的模板结构Child在这里绑定typesParent和inheritance。 任何inheritance自Child<T>类都将从Tinheritance,并将T定义为Parent 。 协变智能指针中使用的类需要定义这种types。

has_parent用于在编译时检测类是否定义了Parenttypes。 这部分不是我的,我使用相同的代码来检测方法是否存在( 请看这里 )

因为我们希望与智能指针协变,我们希望我们的智能指针能够模仿现有的类架构。 在示例中解释它如何工作更容易。

定义CovariantSharedPtr<B> ,它从ICovariantSharedPtr<B>inheritance,它被解释为ICovariantSharedPtr<B, has_parent<B>::value> 。 因为Binheritance自Child<A>has_parent<B>::value真,所以ICovariantSharedPtr<B>ICovariantSharedPtr<B, true>并从ICovariantSharedPtr<B::Parent>inheritance,即ICovariantSharedPtr<A> 。 由于A没有定义Parenthas_parent<A>::value为false, ICovariantSharedPtr<A>ICovariantSharedPtr<A, false>并且从无inheritance。

重点是BAinheritance,我们有从ICovariantSharedPtr<A>inheritance的ICovariantSharedPtr<B> 。 因此,在ICovariantSharedPtr<A>上返回指针或引用的任何方法都可以通过在ICovariantSharedPtr<B>上返回相同的方法来重载。

Fooz先生回答了你的问题的第一部分。 第2部分,它是这样工作的,因为编译器不知道它是否会在编译时调用AInterface :: get_r或A1 :: get_r – 它需要知道它将得到什么样的返回值,所以它坚持两种方法返回相同的types。 这是C ++规范的一部分。

对于解决方法,如果A1 :: get_r返回一个指向RetInterface的指针,则RetInterface中的虚拟方法仍将按预期方式工作,并且在指针被销毁时正确的对象将被删除。 不需要不同的返回types。

也许你可以使用out参数来解决与返回的boost shared_ptrs的协方差。

  void get_r_to(boost::shared_ptr<RetInterface>& ) ... 

因为我怀疑一个调用者可以放入一个更精致的shared_ptrtypes作为参数。