Как да постигнем ковариантни връщани типове при връщане на shared_ptr?

using namespace boost;

class A {};
class B : public A {};

class X {
  virtual shared_ptr<A> foo();
};

class Y : public X {
  virtual shared_ptr<B> foo();
};

Върнатите типове не са ковариантни (нито, следователно, са законни), но биха били, ако вместо това използвах необработени указатели. Какъв е общоприетият идиом за заобикаляне на това, ако има такъв?


person Kyle    schedule 22.04.2010    source източник
comment
възможен дубликат на stackoverflow.com/questions/196733/   -  person Terry Mahaffey    schedule 22.04.2010


Отговори (3)


Мисля, че решението е фундаментално невъзможно, защото ковариацията зависи от аритметиката на указателя, която е несъвместима с интелигентните указатели.

Когато Y::foo връща shared_ptr<B> на динамичен повикващ, той трябва да бъде прехвърлен към shared_ptr<A> преди употреба. Във вашия случай B* може (вероятно) просто да бъде преинтерпретирано като A*, но за множествено наследяване ще ви трябва малко магия, за да кажете на C++ за static_cast<A*>(shared_ptr<B>::get()).

person Potatoswatter    schedule 22.04.2010
comment
Хм, фундаментално ковариацията зависи от преобразуването и може би трябва да работи с имплицитно конвертируеми типове. Но това все още няма да се отнася за shared_ptr. - person Potatoswatter; 16.08.2012
comment
Но това пак няма да се отнася за shared_ptr Не разбирам: shared_ptr не могат ли да се конвертират? - person curiousguy; 28.05.2013
comment
@curiousguy О, опа, така е. Все пак само хипотетично. - person Potatoswatter; 29.05.2013

Не директно, но можете да го фалшифицирате, като направите действителните виртуални функции недостъпни извън класа и обвиете извикването на виртуална функция в невиртуална функция. Недостатъкът е, че ще трябва да запомните да приложите тази функция за обвиване на всеки производен клас. Но можете да заобиколите това, като поставите както декларацията на virtul функция, така и обвивката в макроса.

using namespace boost; // for shared_ptr, make_shared and static_pointer_cast.

// "Fake" implementation of the clone() function.
#define CLONE(MyType) \
    shared_ptr<MyType> clone() \
    { \
        shared_ptr<Base> res = clone_impl(); \
        assert(dynamic_cast<MyType*>(res.get()) != 0); \
        return static_pointer_cast<MyType>(res); \
    }

class Base 
{
protected:
    // The actual implementation of the clone() function. 
    virtual shared_ptr<Base> clone_impl() { return make_shared<Base>(*this); }

public:
    // non-virtual shared_ptr<Base> clone();
    CLONE(Base)
};

class Derived : public Base
{
protected:
    virtual shared_ptr<Base> clone_impl() { return make_shared<Derived>(*this); }

public:
    // non-virtual shared_ptr<Derived> clone();
    CLONE(Derived)
};


int main()
{
    shared_ptr<Derived> p = make_shared<Derived>();
    shared_ptr<Derived> clone = p->clone();

    return 0;
}
person sdkljhdf hda    schedule 12.09.2011
comment
Мина известно време, откакто беше отговорено. След като c++11 излезе, има ли по-добър начин? - person Cory-G; 29.07.2014
comment
@CoryBeutler. Не, ако искате да използвате shared_ptr. Ако сте готови да се откажете от shared_ptr и сте готови да използвате натрапчиво преброяване на препратки, тогава да. В този случай функциите ще върнат необработени указатели и за да получите автоматично преброяване, трябва само да присвоите резултата на интелигентен указател (напр. boost::intrusive_ptr). - person sdkljhdf hda; 29.07.2014

Просто връщам гол указател и го увивам веднага в споделения указател.

person quant_dev    schedule 12.09.2011
comment
Почти никога не искате да обвиете голия указател на някой друг към shared_ptr. - person Shital Shah; 21.12.2016