Сожительство boost::shared_ptr и std::shared_ptr

В какой-то момент я хочу использовать boost::log, но не могу передать std::shared_ptr в качестве параметра, потому что компилятор (VS2010) не может преобразовать его в boost::shared_ptr.

Мне не очень нравится тот факт, что они чужие друг другу.

Есть ли безопасный и прозрачный способ преобразовать одно в другое, чтобы они не спотыкались друг о друга?

Я не думаю, что это дубликат этого вопроса, в котором указаны оба такие же.


person Stephane Rolland    schedule 07.09.2012    source источник
comment
Ответ до C++11 см. в разделе stackoverflow.com/questions/6326757/   -  person janm    schedule 07.09.2012
comment
У меня аналогичная проблема с std::array и boost::array.   -  person alfC    schedule 09.09.2012
comment
@alfC Я не думаю, что есть простой способ преобразования между std::array и boost::array без копирования содержимого массива.   -  person Marshall Clow    schedule 13.10.2013
comment
Может быть, в случае std::shared_ptr и boost::shared_ptrstd::array и boost::array) можно сделать reinterpret_cast?   -  person alfC    schedule 13.10.2013
comment
@alfC: даже если компилятор перестанет жаловаться, это верный способ испортить состояние интеллектуального указателя.   -  person Michael Burr    schedule 06.01.2014
comment
У них должен быть одинаковый интерфейс, почему бы не взломать boost и заменить содержимое boost/shared_ptr.hpp чем-то вроде #include <memory> namespace boost { using shared_ptr = std::shared_ptr?   -  person fghj    schedule 20.11.2015


Ответы (1)


Вы можете сделать это следующим образом:

template<typename T>
boost::shared_ptr<T> make_shared_ptr(std::shared_ptr<T>& ptr)
{
    return boost::shared_ptr<T>(ptr.get(), [ptr](T*) mutable {ptr.reset();});
}

template<typename T>
std::shared_ptr<T> make_shared_ptr(boost::shared_ptr<T>& ptr)
{
    return std::shared_ptr<T>(ptr.get(), [ptr](T*) mutable {ptr.reset();});
}

РЕДАКТИРОВАТЬ: обратите внимание, что это не работает со слабыми ссылками на исходный ptr. Так что будьте осторожны с ними!

person ronag    schedule 07.09.2012
comment
Что за второй параметр для конструкторов? Похоже (из сигнатуры конструктора), что это должно быть средство удаления, но я не понимаю синтаксиса. - person Chowlett; 07.09.2012
comment
Это лямбда-выражение для функции, которая ничего не делает. - person jcoder; 07.09.2012
comment
О, так оно и есть. Теперь я узнаю - нести ptr в прицел (зачем?), брать T*, ничего не делать. Предположительно, дело в том, что общие указатели boost и std не знают о счетчиках друг друга, поэтому не следует уничтожать объект, изначально удерживаемый другим? - person Chowlett; 07.09.2012
comment
Да, я был готов написать комментарий о том, что это ужасно опасно, потому что у вас будет два разных shared_ptr, отвечающих за удаление одной и той же памяти, затем я увидел, что оригинальный автор сделал это правильно :) - person jcoder; 07.09.2012
comment
+1 Вау, действительно хороший трюк, используйте захват по значению, чтобы сохранить копию исходного ptr (и, следовательно, дополнительный счетчик ссылок) внутри лямбды удаления. Мне потребовалось время, чтобы понять это, должен признать. - person Christian Rau; 07.09.2012
comment
Я не могу ясно видеть, как внутренняя ссылка уменьшается, когда пункт назначения (сделанный shared_ptr) уничтожается... нет ли двух одновременных счетчиков ссылок, один для std, другой для повышения ? - person Stephane Rolland; 07.09.2012
comment
Когда пункт назначения уничтожается, его средство удаления (содержащее ссылку на источник) также уничтожается, тем самым уменьшая ссылку на источник. Что касается двух одновременных счетчиков ссылок, время жизни указателя по-прежнему обрабатывается только источником. - person ronag; 07.09.2012
comment
ссылка сохраняется параметром лямбды, чтобы ее можно было использовать (позже) в средстве удаления, не так ли? - person Stephane Rolland; 07.09.2012
comment
а лямбда при этом уничтожается? - person Stephane Rolland; 07.09.2012
comment
@StephaneRolland Средство удаления лямбда хранится как член boost::shared_ptr и, в свою очередь, имеет копию std::shared_ptr как члена (из-за захвата лямбда по значению), что увеличивает счетчик ссылок для исходного объекта. Таким образом, каждый boost::shared_ptr увеличивает счетчик ссылок на исходный объект через его элемент удаления. Но когда счетчик ссылок boost::shared_ptr (который действительно не зависит от счетчика std) достигает 0, ничего не удаляется, так как удаление не является операцией, но уничтожение удаления фактически уменьшает исходный счетчик ссылок. - person Christian Rau; 07.09.2012
comment
@ChristianRau: Только один момент, в некоторых (большинстве?) Реализации средство удаления хранится вместе со счетчиком ссылок (то есть совместно используется всеми экземплярами), поэтому счетчик ссылок на источник увеличивается только один раз, во время построения лямбды средства удаления. - person ronag; 07.09.2012
comment
@ronag Ах, я не был уверен в этом, но, к счастью, это не меняет правильности вашего решения (и на самом деле должно быть хорошо для производительности). Но спасибо за информацию. - person Christian Rau; 07.09.2012
comment
просто чтобы понять: когда объект лямбда уничтожается? - person Stephane Rolland; 08.09.2012
comment
@StephaneRolland: когда счетчик ссылок адресата достигает 0. Вы также можете посмотреть код для std::shared_ptr или boost::shared_ptr и выяснить это. - person ronag; 08.09.2012
comment
да, вы правы, но часто я все еще боюсь читать код STL... но вы правы, я должен начать это делать. - person Stephane Rolland; 08.09.2012
comment
Очень умно, но есть ловушка. Если вы вызываете make_shared_ptr для std::shared_ptr, возвращенного из make_shared_ptr, вы создаете два shared_ptr, которые содержат ссылки друг на друга, таким образом создавая циклическую ссылку. Эти объекты никогда не будут удалены, поскольку они поддерживают друг друга. - person Eric Niebler; 08.09.2012
comment
@EricNiebler: На самом деле проблем нет. Я считаю, что вы не правы, т.е. нет циклической ссылки. Попробуй сам... - person ronag; 08.09.2012
comment
Чтобы избежать утечек памяти, вы должны создать именованный временный файл для boost::shared_ptr. Это также упоминается в рекомендациях [boost. org/doc/libs/1_51_0/libs/smart_ptr/ - person janr; 08.10.2012
comment
janr: Я не думаю, что это уместно в данном случае. - person ronag; 08.10.2012
comment
Подход интересен, если несовершенен. Средство удаления сохраняется вместе со счетчиком и выполняется, когда все ссылки strong выходят за пределы области видимости. Проблема с таким подходом в том, что при наличии weak_ptr будет вызван детерсер, и ничего не произойдет, а пока есть хотя бы тот самый weak_ptr, время жизни объекта будет искусственно продлено ссылкой внутри деллер, нарушение семантики комбинации weak_ptr/shared_ptrcombination. Фикс простой, дизайн хороший, только реализацию надо менять: [ptr](T*){ptr.reset();} - person David Rodríguez - dribeas; 14.10.2013
comment
@DavidRodríguez-dribeas: Действительно, хороший улов! Я обновлю ответ. - person ronag; 14.10.2013
comment
@ronag Разве лямбда теперь не должна быть mutable? (Да, только что подтверждено.) - person dyp; 14.10.2013
comment
Разве это не проблема захвата времени жизни источника shared_ptr? Другими словами, не будет ли исходный shared_ptr иметь счетчик ссылок равным нулю, когда целевой shared_ptr освобождается? - person Ben Collins; 06.01.2014
comment
@ronag ах, я не совсем понял, что захват был по значению. - person Ben Collins; 06.01.2014
comment
@DavidRodríguez-dribeas: я считаю, что даже с вашим исправлением все еще есть недостаток с наличием weak_ptr. Если, например, вы создаете std shared_ptr, затем создаете boost::shared_ptr из него (используя описанный выше метод), затем создаете boost::weak_ptr из boost::shared_ptr, когда boost::shared_ptr удаляется, weak_ptr будет думать, что объект больше не существует (weak_ptr.lock() вернет null ), даже если фактический объект (то есть исходный std::shared_ptr) все еще существует. - person Arieh; 28.11.2014
comment
@Arieh: Да, он изменил запоздалый выпуск из-за слабых ссылок на преждевременную диссоциацию из-за сильных указателей ниже этого уровня. Проблема в принципе неразрешима, если не известны внутренние элементы, а непосредственные члены shared_ptr самое большее переключаются. - person Deduplicator; 19.11.2015
comment
@Arieh: добавлено примечание. - person ronag; 20.11.2015
comment
В этом ответе есть еще один недостаток: если вы используете его дважды (для преобразования, а затем обратно) для одного и того же указателя, вы создадите ссылочный цикл (т.е. утечку памяти). Это лучше решается в этом ответе, хотя с той же оговоркой, что преобразованные weak_ptrs могут истечь раньше. (Я не думаю, что это легко разрешимо.) - person Miral; 03.03.2017
comment
@Miral: Вы не получите эталонный цикл. Но ответ, на который вы ссылаетесь, имеет интересную оптимизацию, если вы много конвертируете туда и обратно. - person ronag; 03.03.2017