У меня есть динамический состав, который терпит неудачу. Расположение классов выглядит так:
class A1
{
public:
virtual int foo1()=0;
};
class A2
{
public:
virtual int foo2();
};
class A3
{
public:
virtual int foo3();
};
class B : public A1, public A2, public A3
{
int bar();
};
Теперь я использую указатели (так что никакое нарезание не может произойти) для снижения.
main()
{
B b;
A1* a1 = dynamic_cast<A1*> (&b); // ok
B* b1 = dynamic_cast<B*> (a1); // ok
A2* a2_1 = dynamic_cast<A2*> (a1); // OSX 10.7 ok, OSX 10.9 fail
A2* a1_2 = dynamic_cast<A2*> (b1); // ok
};
Downcast работает, upcast работает, но sidecast не работает всегда. В OSX 10.7 Sidecast работает, в OSX 10.9 — нет (GCC 4.2 использует динамический c ++ stdlib). Глядя на vtable с помощью gdb, я ясно вижу перечисленные методы и члены A2.
Мои вопросы:
а) Является ли Sidecast технически правильным? Должно ли это работать или это ошибка времени выполнения?
б) Если побочная трансляция зависит от времени выполнения, где определяется время выполнения? Я всегда думал, что это часть двоичного файла?
в) Как выглядит сбой dynamic_cast (при просмотре сборки), и как вы можете отследить такие проблемы?
Обновление: это то, что я вижу в системной консоли
01.11.15 14: 16: 27,435 APPNAMECHANGED [15280]: ошибка dynamic_cast 1: Обе из следующих type_info должны иметь публичную видимость. По крайней мере, один из них скрыт. 10А1, 15А2.
Итак, в) ответили, по крайней мере, для OSX 10.9. Посмотрите на консоль. (Linux, кто-то?)
Похоже, проблема с видимостью символа. Док говорит (gcc.gnu.org/wiki/Visibility)
Тем не менее, это не полная история — это становится сложнее. По умолчанию видимость символа «по умолчанию», но если компоновщик встречает только одно определение со скрытым — только одно — этот символ typeinfo становится постоянно скрытым (помните ODR стандарта C ++ — правило одного определения). Это верно для всех символов, но, скорее всего, повлияет на вас с помощью typeinfos; Символы typeinfo для классов без vtable определяются по требованию в каждом объектном файле, который использует класс для EH, и определяются слабо, поэтому определения объединяются во время ссылки в одну копию.
Это приводит к следующему вопросу,
г) как мы можем отследить, какой символ хотя бы раз отмечен как скрытый, и где и почему? Есть ли инструмент для проверки .o файлов?
Отказ от ответственности: я только собираюсь ответить на вопрос а).
Прежде всего, B
это явно полиморфный тип в соответствии с [class.virtual] / 1, поскольку он наследует виртуальную функцию (если быть точным, B
наследует три отдельных).
Теперь рассмотрим [expr.dynamic.cast] / 8:
Если
C
это тип класса, к которомуT
указывает или ссылается, время выполнения
Проверка логически выполняется следующим образом:
- Если в наиболее производном объекте, на который указывает (указано) значение v, то v указывает (указывает) на открытый базовый класс
подобъектC
объект, и если только один объект типаC
происходит от подобъекта
чтобы поv
результат указывает (относится) к этомуC
объект.- Иначе, если
v
указывает (ссылается) на подобъект общедоступного базового класса самого производного объекта и тип самого производного объекта
имеет базовый класс, типаC
однозначно и публично,
результат указывает (относится) кC
подобъект самого производного
объект.- В противном случае проверка во время выполнения терпит неудачу.
Так что да, это должно работать.
Эта проблема была окончательно решена после того, как мы обнаружили, что исполняемый файл связан с dylib, который был статически связан со средой выполнения c ++, но был скомпилирован с другим компилятором (gcc 4.8 против clang).