dynamic_cast с отключенным RTTI

#c #rtti #dynamic-cast

#c #rtti #динамическое приведение

Вопрос:

Мне любопытно узнать, что происходит при компиляции кода с динамическим приведением с отключенным RTTI (либо с -fno-rtti помощью GCC, либо с /GR- помощью Visual Studio). Компилятор «возвращается» к static_cast ? Поскольку (по крайней мере, в VS) он выдает только предупреждение, что будет делать скомпилированный код?

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

 class A{ /*...*/ } ;
class B : public A {
    int foo() { return 42 ;}
} ;
//...
A * myA = new B() ;
int bar = (dynamic_cast<B*>(myA))->foo() ;
  

Комментарии:

1. Почему бы просто не попробовать?

2. Почему ваш код использует, dynamic_cast если вы уверены, что это static_cast можно использовать вместо этого?

3. @edA-qamort-ora-y: Я знаю, что это лучше (и на самом деле это то, что я сделал с оскорбительным кодом), я просто хотел знать, что произойдет

4. @EranZimmerman : Я выяснил, что был выброшен объект std::__non_rtti_, но только в некоторых случаях (я до сих пор не знаю, почему)

5. @EranZimmerman Хотелось бы знать, всегда ли это будет работать на любой платформе и компиляторе, а не готовить тикающую бомбу, готовую взорваться при переключении компилятора.

Ответ №1:

Читая стандарт, в 5.2.7 / 6 мы обнаруживаем, что, если целевой объект не является однозначной базой источника, источник должен быть полиморфного типа. Затем в 10.3 / 1

Виртуальные функции поддерживают динамическое связывание и объектно-ориентированное программирование. Класс, который объявляет или наследует виртуальную функцию, называется полиморфным классом.

Другими словами, стандарт, похоже, ничего не говорит о вашем вопросе. В этом случае стандарт не позволяет компилятору отключать RTTI, поэтому для каждого компилятора вам нужно проверить его документацию, чтобы увидеть, что произойдет. Основываясь на этом чтении, я думаю, что это вопрос компилятора, а не вопрос языка C , как указывает тег.

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

Ответ №2:

В MSVC, если ваш код не скомпилирован с включенным RTTI, будет выдано __non_rtti_object исключение, если приведение не может быть выполнено без проверки во время выполнения.

Ответ №3:

Самый простой способ узнать это — попробовать.

Вы обнаружите, что некоторые из ваших динамических приведений будут помечены как незаконные. Некоторые не будут. Например, преобразование известно во время компиляции, когда вы используете динамическое приведение для преобразования в однозначный базовый класс.

Добавление
Повторно «Поскольку (по крайней мере, в VS) он выдает только предупреждение …» Игнорируйте предупреждения на свой страх и риск. Лучшее, что нужно сделать, это убедиться, что ваш код компилируется без предупреждений, с очень высокими уровнями предупреждения (и, возможно, преобразованными в ошибки). Во-вторых, лучше всего просмотреть каждое получаемое вами предупреждение и убедиться, что ничего плохого не происходит. В этом случае произойдет что-то нежелательное. Вас действительно не должно волновать, как реализуется это нежелательное событие. О чем вам следует позаботиться, так это избавиться от него.

Комментарии:

1. Я знаю, что обычно лучше удалить причину предупреждения, чем жить с этим. Что я имел в виду под «он выдает только предупреждение», так это то, что компилятор все еще принял (но не одобрил) код и скомпилировал его во что-то.

2. Что это за «что-то», зависит от реализации. Стандарт является спорным, когда дело доходит до отключения RTTI.

Ответ №4:

Просто попробуйте:

 #include <iostream>
#include <typeinfo>
#include <typeindex>
#include <memory>
#include <vector>
#include <array>
#include <string>

class Base {
public:
  virtual ~Base() {
  }
};

class A: public Base {
};

class B: public Base {
};

using namespace std;

int main() {
  A *a = new A;
  auto *ptr = dynamic_cast<B*>(a);

  if (!ptr)
    std::cout << "failed to cast" << std::endl;

  return 0;
}
  

Без -fno-rtti программа компилируется, и результат является:

 failed to cast
  

С -fno-rtti не удалось скомпилировать программу:

 main.cpp:25:35: error: ‘dynamic_castnot permitted with -fno-rtti
     auto* ptr = dynamic_cast<B*>(a);
                                   ^
  

Вы также можете протестировать это онлайн здесь:https://onlinegdb.com/pYTQu2ne2

Комментарии:

1. Проблема с выводом правил по поведению компилятора вы можете столкнуться с ошибкой компилятора и полагаться на неправильные правила.