Как добиться ковариантных возвращаемых типов при возврате 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
Прошло некоторое время с тех пор, как на это ответили. Теперь, когда С ++ 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