كيف يمكنني استخدام أنواع الإرجاع طردي مع المؤشرات الذكية?

StackOverflow https://stackoverflow.com/questions/196733

  •  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 للاستخدام في الفصول الأخرى ، يجب أن يلقي القيمة التي تم إرجاعها بعد العودة.

  1. أفعل شيئا خاطئا ؟
  2. إذا لم يكن كذلك ، لماذا هي لغة مثل هذا - يجب أن تكون الموسعة للتعامل مع التحويل بين المؤشرات الذكية في هذا السيناريو ؟ هل هناك من المرغوب فيه الحل?
هل كانت مفيدة؟

المحلول

أولا, هذا هو في الواقع كيف يعمل في 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 نوع الحجة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top