Введите стирание в C++: как работают boost::shared_ptr и boost::function?

Тип стирания - это как вы это называете?

Как boost::shared_ptr хранит свой объект удаления и как boost::function хранит свой функциональный объект?

Есть ли какой-нибудь учебник, который учит трюку?

Какова стоимость во время выполнения использования функциональных объектов с стертым типом?


person pic11    schedule 12.06.2011    source источник
comment
Вы пробовали гуглить, прежде чем спрашивать? в сети много информации. старая статья Тамаса Беккера: artima.com/cppsource/type_erasure.html или вот эта известная книга: boostpro.com/mplbook и множество других ресурсов.   -  person Gene Bushuyev    schedule 13.06.2011
comment
@GeneBushuyev: вся цель SO состоит в том, чтобы сделать поиск в Google ненужным. Если вы хотите узнать о каком-то предмете, связанном с программированием, вы можете найти его в Google и либо (1) найти SO-ответ, либо (2) получить недостоверную информацию, которая может быть или не быть правильной и помочь вам. Или вы можете (2) искать/спрашивать об этом на SO и получать ответы, которые рецензируются экспертами, оцениваются по качеству и практически гарантированно будут полезными. Пожалуйста, не говорите людям в Google вместо того, чтобы задавать вопросы здесь. Это контрпродуктивно.   -  person jalf    schedule 04.03.2012


Ответы (1)


Идея проста, вы определяете базовый класс, который имеет интерфейс с нужным вам функционалом, а затем наследуетесь от него. Поскольку класс type eraser использует только этот интерфейс, фактический тип под ним забыт и стерт. В качестве альтернативы, если единственный необходимый интерфейс может быть выражен в виде свободных функций, вы можете хранить указатели на свободные функции.

namespace detail {
   struct deleter_base {
      virtual ~deleter_base() {}
      virtual void operator()( void* ) = 0;
   };
   template <typename T>
   struct deleter : deleter_base {
      virtual void operator()( void* p ) {
         delete static_cast<T*>(p);
      }
   };
}
template <typename T>
class simple_ptr {
   T* ptr;
   detail::deleter_base* deleter;
public:
   template <typename U>
   simple_ptr( U* p ) {
      ptr = p;
      deleter = new detail::deleter<U>();
   }
   ~simple_ptr() {
      (*deleter)( ptr );
      delete deleter;
   }
};

Это действительно упрощенный умный указатель, но идея есть. В частном случае shared_ptr deleter сохраняется как часть объекта счетчика ссылок, который удерживается указателем.

person David Rodríguez - dribeas    schedule 12.06.2011
comment
Средство удаления shared_ptr не должно быть производным от чего-либо. - person pic11; 13.06.2011
comment
@pic11: Средство удаления, переданное в shared_ptr, не нуждается в наследовании, но стирание типов реализуется с использованием внутреннего наследования (или указателей функций). - person David Rodríguez - dribeas; 13.06.2011
comment
Как вы вызываете функциональный объект, если вы не знаете его статический тип и/или он не наследуется от базового класса? - person pic11; 13.06.2011
comment
@pic11: Вот недавний ответ, который я дал об использовании стирания типа, взгляните, чтобы понять, о чем говорит Дэвид в коде. - person GManNickG; 13.06.2011
comment
Разве deleter не должно происходить от deleter_base в этом примере? На данный момент это не так. - person Sjoerd; 13.06.2011
comment
@Sjoerd: Спасибо, вы правы, я пропустил отношения наследования (вместе с пространством имен ...). - person David Rodríguez - dribeas; 13.06.2011
comment
Спасибо всем. Мне нужно время, чтобы переварить материал. - person pic11; 13.06.2011
comment
@David В деструкторе simple_ptr вы вызываете operator(), но deleter является указателем, поэтому строка должна читаться как (*deleter)(ptr);. Может быть, было бы лучше иметь обычную функцию-член вместо operator(). Обычно я называю эти одноцелевые операции остроумным названием вроде doIt(), execute() или makeItHappen() :-) - person Fiktik; 03.03.2012
comment
из первого абзаца вашего ответа: the actual type underneath is forgotten and erased ‹-- не могу этого понять, пожалуйста, объясните простым языком? Спасибо - person Mr.Anubis; 26.07.2012
comment
@Mr.Anubis: Тип shared_ptr/function не отражает тип функтора deleter/, который использовался для его создания. Когда вы получаете объекты shared_ptr или function, типы, использованные для их создания, больше не доступны. - person David Rodríguez - dribeas; 26.07.2012
comment
@DavidRodríguez-dribeas Прошу прощения за поздний ответ. Не могли бы вы немного полегче со мной? . Я понял пример, т. е. интерфейсы и виртуальные функции делают магию под капотом и как все работает, но ваши слова / предложения не приходят мне в голову, например, the types used to create it are no longer available. - person Mr.Anubis; 30.07.2012
comment
@Mr.Anubis: Может быть, проще на примере: void f( shared_ptr<int> ) и два общих указателя: shared_ptr<int> p( new int ); int i; shared_ptr<int> q(&i, noop_deleter); (при условии соответствующего удаления). Удалители не участвуют в типе shared_ptr, а это значит, что вы можете делать как f(p), так и f(q). Сравните это с std::unique_ptr, где средство удаления является аргументом шаблона: void f( unique_ptr<int> ) и unique_ptr<int,noop> q( &i ), тогда f(q) является ошибкой. Тип удаления является частью типа интеллектуального указателя. - person David Rodríguez - dribeas; 30.07.2012
comment
... в первом случае тип удалятеля erased и тип умного указателя это не отражает, во втором случае тип удалятеля является частью типа умный указатель. - person David Rodríguez - dribeas; 30.07.2012
comment
@DavidRodríguez-dribeas круто!!, этот пример сделал все более ясным, большое спасибо за быструю помощь и ваше терпение :) - person Mr.Anubis; 30.07.2012
comment
Этот ответ хорош для иллюстрации, но на самом деле семантика shared_ptr не требует, чтобы p был захвачен в вспомогательном классе удаления-захвата, не предоставленном позже? (возможно, std::shared_ptr и boost::shared_ptr отличаются в этом плане) - person Ben Voigt; 04.09.2014