dynamic_cast 失败 - 取决于 OS 版本

dynamic_cast fails - depending on OS Version

我的动态转换失败了。 class 布局是这样的:

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)。使用 gdb 查看 vtable 我可以清楚地看到列出的 A2 方法和成员。

我的问题:

a) 侧播在技术上是否正确?它应该工作还是这是一个运行时错误?
b) 如果 sidecast 依赖于运行时,这个运行时是在哪里定义的?我一直以为这是二进制文件的一部分?
c) 失败的 dynamic_cast 是什么样子的(在查看程序集时),您如何追踪此类问题?

更新:这是我在系统控制台看到的

11/01/15 14:16:27,435 APPNAMECHANGED[15280]: dynamic_cast error 1: Both of the following type_info's should have public visibility. At least one of them is hidden. 10A1, 15A2.

所以 c) 得到了回答,至少对于 OSX 10.9。看看控制台。 (Linux,有人吗?) 看起来像是关于符号可见性的问题。医生说 (gcc.gnu.org/wiki/Visibility)

However, this isn't the full story - it gets harder. Symbol visibility is "default" by default but if the linker encounters just one definition with it hidden - just one - that typeinfo symbol becomes permanently hidden (remember the C++ standard's ODR - one definition rule). This is true for all symbols, but is more likely to affect you with typeinfos; typeinfo symbols for classes without a vtable are defined on demand within each object file that uses the class for EH and are defined weakly so the definitions get merged at link time into one copy.

这就引出了下一个问题,

d) 我们怎样才能追踪到哪个符号至少被标记为隐藏一次,在哪里以及为什么?是否有检查 .o 文件的工具?

免责声明:我只会解决问题 a)。

首先,根据[class.virtual]/1,B显然是一个多态类型,因为它继承了一个虚函数(准确地说,B继承了三个不同的虚函数).
现在考虑 [expr.dynamic.cast]/8:

If C is the class type to which T points or refers, the run-time check logically executes as follows:

  • If, in the most derived object pointed (referred) to by v, v points (refers) to a public base class subobject of a C object, and if only one object of type C is derived from the subobject pointed (referred) to by v the result points (refers) to that C object.
  • Otherwise, if v points (refers) to a public base class subobject of the most derived object, and the type of the most derived object has a base class, of type C, that is unambiguous and public, the result points (refers) to the C subobject of the most derived object.
  • Otherwise, the run-time check fails.

所以是的,它应该有效。

在我们发现可执行文件链接到 dylib 后,问题终于得到解决,dylib 本身静态链接到 c++ 运行时,但使用不同的编译器(gcc 4.8 vs clang)编译。