Отказ от dynamic_cast для приведения к исходному типу

Как я могу безопасно приводить (т.е. возвращать null в случае сбоя) к точному типу базового объекта, не подвергаясь штрафу за производительность dynamic_cast и не добавляя код поддержки в каждый используемый класс?


person Dan    schedule 15.07.2012    source источник
comment
Если бы это было возможно, то почему бы dynamic_cast не реализовать этот метод?   -  person Konrad Rudolph    schedule 15.07.2012
comment
@KonradRudolph dynamic_cast строго мощнее. По сути, это особый случай использования.   -  person Dan    schedule 15.07.2012
comment
@KonradRudolph: он изменил вопрос.   -  person Nicol Bolas    schedule 15.07.2012
comment
Я не понимаю, как это имеет значение, но сейчас я предполагаю, что я просто что-то упускаю из виду. Может быть, я просто неправильно понимаю «точный тип базового объекта». Был бы полезен пример кода с запрошенной семантикой (и, что более важно, то, что он не должен делать).   -  person Konrad Rudolph    schedule 15.07.2012
comment
@KonradRudolph это используется только тогда, когда вам нужно преобразовать базовый указатель в тот же самый тип, которым на самом деле является базовый объект (т. Е. Не какие-либо промежуточные классы).   -  person Seth Carnegie    schedule 15.07.2012
comment
Измеряли ли вы стоимость dynamic_cast, когда операнд имеет точно правильный тип?   -  person Alan Stokes    schedule 15.07.2012
comment
@AlanStokes Да, это в ответе.   -  person Dan    schedule 15.07.2012


Ответы (1)


dynamic_cast будет проходить по всему дереву наследования, чтобы увидеть, возможно ли желаемое преобразование. Если все, что вам нужно, это прямое приведение к типу того же, что и объект, и вам не нужна возможность перекрестного приведения, приведения через виртуальное наследование или приведения к базовому классу объекта фактический тип объекта, будет работать следующий код:

template<class To>
struct exact_cast
{
    To result;

    template<class From>
    exact_cast(From* from)
    {
        if (typeid(typename std::remove_pointer<To>::type) == typeid(*from))
            result = static_cast<To>(from);
        else
            result = 0;
    }

    operator To() const
    {
        return result;
    }
};

Семантика точно такая же, как и для других операторов приведения, т.е.

Base* b = new Derived();
Derived* d = exact_cast<Derived*>(b);

Изменить: я протестировал это в проекте, над которым работаю. Мои результаты от QueryPerformanceCounter:
dynamic_cast: 83 024 197
exact_cast: 78 366 879
Это ускорение на 5,6%. Это для нетривиального кода, привязанного к процессору. (Он не выполняет ввод-вывод)

person Dan    schedule 15.07.2012
comment
Есть ли доказательства того, что это более эффективно, чем dynamic_cast? - person Oliver Charlesworth; 15.07.2012
comment
Кроме того, почему класс вместо функции? - person avakar; 15.07.2012
comment
@OliCharlesworth: я не удивлюсь, если это будет более эффективно (получение typeinfo должно быть всего в нескольких разыменованиях, без больших вычислений). - person Matthieu M.; 15.07.2012
comment
Этот код оказался на 0,05 секунды быстрее по сравнению с dynamic_cast в моем тесте на 4 000 000 итераций построения полиморфного объекта в куче и присвоения базовому указателю, приведения к производному и его освобождения (с уровнем оптимизации -Ofast в gcc 4.7. 1). - person Seth Carnegie; 15.07.2012
comment
@Сет Карнеги: я неправильно понял код. Я удалил комментарий. - person AnT; 15.07.2012
comment
@Nicol Bolas: Это, вероятно, то, что OP имел в виду, говоря, что все, что вам нужно, это прямое приведение к типу того же, что и объект. Это же должно означать, что вам разрешено приводить только к наиболее производному типу объекта, а не к промежуточным базам. Это, конечно, сильно ограничивает возможности использования данного приведения. - person AnT; 15.07.2012
comment
@AndreyT Вот что я имел в виду. Я уточню это. - person Dan; 15.07.2012
comment
@NicolBolas Это правда. Я обновил вопрос до того, что я хотел сказать. - person Dan; 15.07.2012