如何使用智能指针协变返回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
返回值。
- 难道我做错了什么?
- 如果没有,为什么这样的语言 – 在这种情况下,应该可以扩展处理智能指针之间的转换? 有没有一个理想的解决方法?
首先,这确实如何在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>
类都将从T
inheritance,并将T
定义为Parent
。 协变智能指针中使用的类需要定义这种types。
类has_parent
用于在编译时检测类是否定义了Parent
types。 这部分不是我的,我使用相同的代码来检测方法是否存在( 请看这里 )
因为我们希望与智能指针协变,我们希望我们的智能指针能够模仿现有的类架构。 在示例中解释它如何工作更容易。
定义CovariantSharedPtr<B>
,它从ICovariantSharedPtr<B>
inheritance,它被解释为ICovariantSharedPtr<B, has_parent<B>::value>
。 因为B
inheritance自Child<A>
, has_parent<B>::value
真,所以ICovariantSharedPtr<B>
是ICovariantSharedPtr<B, true>
并从ICovariantSharedPtr<B::Parent>
inheritance,即ICovariantSharedPtr<A>
。 由于A
没有定义Parent
, has_parent<A>::value
为false, ICovariantSharedPtr<A>
为ICovariantSharedPtr<A, false>
并且从无inheritance。
重点是B
从A
inheritance,我们有从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作为参数。