كيف يمكنني استخدام أنواع الإرجاع طردي مع المؤشرات الذكية?
-
10-07-2019 - |
سؤال
لدي رمز مثل هذا:
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 {...}
...
};
هذا الرمز لا ترجمة.
في visual studio أنه يثير
C2555:تجاوز دالة الظاهري عودة نوع يختلف وليس covariant
إذا كنت لا تستخدم boost::shared_ptr
ولكن العودة الخام المؤشرات ، البرمجية برمجيا (أنا أفهم هذا يرجع إلى أنواع الإرجاع طردي في C++).أستطيع أن أرى المشكلة بسبب boost::shared_ptr
من Ret1
ليست مشتقة من boost::shared_ptr
من RetInterface
.ولكن أريد أن أعود boost::shared_ptr
من Ret1
للاستخدام في الفصول الأخرى ، يجب أن يلقي القيمة التي تم إرجاعها بعد العودة.
- أفعل شيئا خاطئا ؟
- إذا لم يكن كذلك ، لماذا هي لغة مثل هذا - يجب أن تكون الموسعة للتعامل مع التحويل بين المؤشرات الذكية في هذا السيناريو ؟ هل هناك من المرغوب فيه الحل?
المحلول
أولا, هذا هو في الواقع كيف يعمل في C++:عودة نوع من الظاهرية وظيفة في فئة مشتقة يجب أن تكون هي نفسها كما في الفئة الأساسية.هناك استثناء خاص أن الدالة التي ترجع إشارة/مؤشر إلى بعض الصف العاشر يمكن تجاوزها من خلال وظيفة بإرجاع مرجع/مؤشر إلى فئة مشتقة من X, ولكن كما يمكنك ملاحظة هذا لا يسمح الذكية المؤشرات (مثل shared_ptr
) فقط عادي المؤشرات.
إذا كان الخاص بك واجهة RetInterface
شاملة بما فيه الكفاية, فإنك لن تحتاج إلى معرفته الفعلية عاد اكتب في رمز الدعوة.عموما هذا لا معنى له على أي حال:السبب get_r
هو virtual
وظيفة في المقام الأول لأنك سوف تكون الدعوة من خلال مؤشر أو الإشارة إلى قاعدة الطبقة AInterface
, في هذه الحالة لا يمكنك أن تعرف ما هو نوع فئة مشتقة سيعود.إذا كنت تتصل هذه الصورة A1
المرجعية ، يمكنك فقط إنشاء منفصلة get_r1
وظيفة في A1
لا ما تحتاج.
class A1: public AInterface
{
public:
boost::shared_ptr<RetInterface> get_r() const
{
return get_r1();
}
boost::shared_ptr<Ret1> get_r1() const {...}
...
};
وبدلا من ذلك يمكنك استخدام الزائر نمط أو شيء من هذا القبيل بلدي الديناميكية المزدوجة الإرسال تقنية لتمرير رد في إرجاع كائن التي يمكن أن ثم استدعاء رد مع النوع الصحيح.
نصائح أخرى
ماذا عن هذا الحل:
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);
}
};
هـ.g:
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++. 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();
}
تفسيرات :
هذا الحل يعني ميتا-progamming وتعديل الطبقات المستخدمة في التغاير المؤشرات الذكية.
بسيطة قالب البنية Child
هنا ربط نوع Parent
والميراث.أي فئة من وراثة Child<T>
سوف ترث من T
وتحديد T
كما Parent
.الطبقات المستخدمة في التغاير المؤشرات الذكية يحتاج هذا النوع إلى تعريف.
الطبقة has_parent
يستخدم للكشف في وقت الترجمة إذا كانت الدرجة يحدد نوع Parent
أو لم يكن.هذا الجزء ليس لي, لقد استخدمت نفس الكود كما أن الكشف عن ما إذا كان الأسلوب موجود (انظر هنا)
كما نريد التغاير مع المؤشرات الذكية ، نريد الذكية لدينا مؤشرات إلى تقليد القائمة فئة العمارة.أنه من الأسهل لشرح كيف يعمل في المثال.
عندما CovariantSharedPtr<B>
يتم تعريف ، فإنه يرث من ICovariantSharedPtr<B>
, يفسر ICovariantSharedPtr<B, has_parent<B>::value>
.كما B
يرث من Child<A>
, has_parent<B>::value
هو صحيح ، لذا ICovariantSharedPtr<B>
هو ICovariantSharedPtr<B, true>
و يرث من ICovariantSharedPtr<B::Parent>
وهو ICovariantSharedPtr<A>
.كما A
لا Parent
محددة ، has_parent<A>::value
هي كاذبة ، ICovariantSharedPtr<A>
هو ICovariantSharedPtr<A, false>
و يرث من لا شيء.
النقطة الرئيسية هي كما B
يرث من A
, لدينا ICovariantSharedPtr<B>
وراثة من ICovariantSharedPtr<A>
.لذلك أي طريقة إرجاع مؤشر أو إشارة على ICovariantSharedPtr<A>
يمكن أن يكون مثقلا طريقة عودته نفسه على ICovariantSharedPtr<B>
.
هناك حل أنيق وشارك في هذا بلوق وظيفة (من راؤول بورخيس)
مقتطفات قليلا قبل إضافة دعم mulitple الميراث و مجردة طرق هي:
template <typename Derived, typename Base>
class clone_inherit<Derived, Base> : public Base
{
public:
std::unique_ptr<Derived> clone() const
{
return std::unique_ptr<Derived>(static_cast<Derived *>(this->clone_impl()));
}
private:
virtual clone_inherit * clone_impl() const override
{
return new Derived(*this);
}
};
class concrete: public clone_inherit<concrete, cloneable>
{
};
int main()
{
std::unique_ptr<concrete> c = std::make_unique<concrete>();
std::unique_ptr<concrete> cc = b->clone();
cloneable * p = c.get();
std::unique_ptr<clonable> pp = p->clone();
}
وأود أن أشجع قراءة المقال كاملا.ببساطة كتابة وشرح جيد.
السيد Fooz أجاب الجزء 1 من سؤالك.الجزء 2, يعمل بهذه الطريقة لأن المترجم لا أعرف ما إذا كان سوف تكون الدعوة AInterface::get_r أو A1::get_r في وقت الترجمة - فإنه يحتاج إلى معرفة ما قيمة الإرجاع هو الذهاب الى الحصول عليها ، لذلك تصر على كلتا الطريقتين العائدين من نفس النوع.هذا هو جزء من C++ المواصفات.
للحصول على الحل ، إذا A1::get_r عودة المؤشر إلى RetInterface ، طرق افتراضية في RetInterface سوف لا تزال تعمل كما هو متوقع, و جسم سليم سيتم حذف عندما المؤشر هو تدميرها.ليس هناك ضرورة العودة مختلفة الأنواع.
ربما يمكنك استخدام المعلمة للالتفاف "التغاير مع عاد دفعة shared_ptrs.
void get_r_to(boost::shared_ptr<RetInterface>& ) ...
منذ أن كنت تشك في وجود المتصل يمكن أن تسقط في أكثر دقة shared_ptr نوع الحجة.