Pull to refresh

Comments 25

Раз уж С++ во все поля — заверните base_holder* held_; в классе any в unique_ptr.
dynamic_cast работает только на полиморфных типах же.
ну я думаю, что base_holder вполне такой
Как с помощью dynamic_cast в функции cast() определить совпадает ли тип U и тип хранимого в холдере объекта?
ну в голову приходит как-то так:

template<typename U>
U cast() const
{
auto candidate = dynamic_cast<holder<U>*> (held_);
if(!candidate) throw std::runtime_error(«Bad any cast»);
return candidate->t_;
}
typeid, примененный к типу, отработает в compile time и в рантайме останется только сравнить два объекта type_info (что скорее всего сведется к сравнению 2х строк).
внути dynamic_cast я бы просто сравнивал два указателя на деструктор из виртуальной таблицы, если он там есть. или что-нибудь еще. даже сравнение type_info будет наверное сравнивать просто два числа или указателя, без всяких строк.

в реальности способ с dynamic_cast будет медленно наверное процентов на 20%. Например, за счет того что может проверятся наличие наследников типа, даже если их нет на самом деле.

но я не думаю, что автор гнался за скоростью — иначе бы сравнил с dynamic_cast и объяснил выбор. вот я и спросил свой вопрос.
В данном варианте можно отключить RTTI в настройках компилятора (зачем? отдельный вопрос). А dynamic_cast без RTTI не работает. Если это библиотечный код общего назначения, то именно так и должно быть реализовано.
Вы ошиблись насчет RTTI:
Для этого, к сожалению, нам придется воспользоваться RTTI.

return typeid(t_);


В этом варианте использования typeid не задействует механизм RTTI, т.к. реальный статический тип переменной t_ известен на этапе компиляции (RTTI включается, когда typeid принимает ссылку на полиморфный тип). Вы можете в этом убедиться, отключив в настройках компиляции поддержку RTTI.

Я бы поправил код вот так, чтобы избежать сомнений:
return typeid(T);
То есть эта магия будет работать даже тогда, когда разные модули С++ собранны разными компиляторами и с разными настройками экспорта? На чем может спотыкаться dynamic_cast в windows.
Вряд ли. std::type_info в разных компиляторах скорее всего будет реализован по-разному. Поэтому, сравнивать 2 ссылки на type_info от разных компиляторов опасно для приложения.
Там вроде бы манглинг имен по разному реализован. Но хотя бы избежать проблем с разными флагами типа -fvisibility поможет?
Я никогда не пробовал дружить код разных компиляторов на уровне классов C++, поэтому не смогу что-то Вам посоветовать. Но уверен, проблем с этим выше крыши. Даже при передаче банального std::string через границу модулей, собранных разными компиляторами, нет никаких гарантий совместимости. А точнее, гарантированы проблемы: бинарной совместимости нет.
Ооо, std перебросить через границу модуля для разных компиляторов это отдельная тема и с этим стараются не связываться или, по крайней мере, сделать все методы использующие std отключаемые по дейфайну.

Вообще стараются переменные шаблонных классов за границу модуля не передавать. Приходиться писать свои велосипеды вместо стандартных контейнеров и им подобных
Да, вы правы. В примере действительно typeid отработает в compile time. Тут скорее речь шла о том, что сравнение типов все равно остается в рантайме, что медленнее полностью статической проверки, которая, к тому же, просто не дала бы скомпилироваться неверному приведению.
На понял, какие преимущества сулит этот подход против куда более простой обёртки над void* (если не использовать специализированные варианты для различных типов):

class any {
 public:
  any(void* ptr) { m_any = ptr; }
  template<class T>T* cast() { return dynamic_cast<T*>(m_any); }
 private:
  void* m_any; 
}
Благодаря этому подходу, реализованы std::function, boost::function, boost::any. Про преимущества boost::any можно разговаривать, но без (std|boost)::function жизнь в C++ тяжела.

А Вы пробовали компилировать Ваш код? dynamic_cast от void* не работает. dynamic_cast работает на полиморфных типах.
Хотите получить представление о том, как устроен boost::function, boost::any “под капотом”?

а где же?
Спасибо за статью!
Зачастую бывает тяжело разобраться в таких конструкциях, а главное понять зачем именно так сделано. Но здесь все объяснено довольно доходчиво!
Начинаю использовать boost в работе, поэтому подобные статьи были бы очень кстати.
Спасибо за статью, однако стоило бы поподробнее раскрыть вот этот упомянутый момент: «Или хранить внутри одного объекта “нечто” произвольного типа с единственным ограничением — наличием оператора “()” у хранимого “нечто”. Не понимаю, как приведенная реализация поможет в решении этой проблемы.
Мне кажется, any::operator() достаточно тривиально реализуется через виртуальный holder::operator(). Заодно получим проверку того, что переданный тип T подходит на этапе компиляции.
В этом предложении имелся в виду boost (std) function. В конце статьи про это немного написано. А под «ограничением» понимается скорее не ограничение хранения, а ограничение дальнейшего использования. Никто не запрещает хранить в function не callable объект. Но особого смысла в этом нет.
Sign up to leave a comment.

Articles