共享库中的模板类的显式实例化和dynamic_cast

问题描述

|| 我今天遇到一个我似乎无法解决的问题。我正在编译一个共享库,其中包括一个模板化类(
Derived<T>
,其基数为
Base
)和该类的一些显式实例化。我希望库用户从此模板化类扩展。当我尝试将用户实例的“ 2”从“ 3”扩展到“ 4”时,就会出现问题。 我将问题缩小到此MWE: 共享库包含以下文件: 基数
#ifndef BASE_H_
#define BASE_H_

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

#endif /* BASE_H_ */
派生
#ifndef DERIVED_H_
#define DERIVED_H_    
#include <Base.h>

template <typename T>
class Derived : public Base {
public:  
    Derived();
    virtual ~Derived();
};

#endif /* DERIVED_H_ */
派生文件
#include <Derived.h>

template <typename T>
Derived<T>::Derived() :
    Base() {
}

template <typename T>
Derived<T>::~Derived() {
}

// explicit instantiations
template class Derived<float>;
template class Derived<double>;
template class Derived<long double>;
助手
#ifndef HELPER_H_
#define HELPER_H_

#include <Base.h>

class Helper {
public:
    Helper(Base* m);
    virtual ~Helper();

};

#endif /* HELPER_H_ */
Helper.cpp
#include <Helper.h>
#include <Base.h>
#include <Derived.h>

#include <iostream>

using namespace std;

Helper::Helper(Base* m) {

    cout << \"after received \" << m << endl;
    cout << \"after fom: \" <<  dynamic_cast< Derived<float>* >(m) << endl;
    cout << \"after dom: \" <<  dynamic_cast< Derived<double>* >(m) << endl;
    cout << \"after ldom: \" <<  dynamic_cast< Derived<long double>* >(m) << endl;
    cout << \"===\" << endl;
}

Helper::~Helper() {
}
使用该库的简单代码可能是: 测试文件
#include <Derived.h>
#include <Helper.h>

#include <iostream>

using namespace std;

class MyModel : public Derived<double> {
public:
    MyModel() : Derived<double>() {
    };

    virtual ~MyModel() {
    };        

};

int main(int argc,char *argv[]) {

    MyModel om1;
    cout << \"created mymodel \" << &om1 << endl;
    cout << \"before fom: \" <<  dynamic_cast< Derived<float>* >(&om1) << endl;
    cout << \"before dom: \" <<  dynamic_cast< Derived<double>* >(&om1) << endl;
    cout << \"before ldom: \" <<  dynamic_cast< Derived<long double>* >(&om1) << endl;
    cout << \"===\" << endl;
    Helper root(&om1);

    return 0;
}
问题是,当我创建一个共享库并链接link11ѭ时,the2ѭ会失败。这是示例输出
created mymodel 0x7fff5fbff3e0
before fom: 0
before dom: 0x7fff5fbff3e0
before ldom: 0
===
after received 0x7fff5fbff3e0
after fom: 0
after dom: 0  // <<< Here I expected it to succeed and return a non-null pointer
after ldom: 0
===
但是,如果我将整个库和示例一起编译,则转换成功:
created mymodel 0x7fff5fbff3e0
before fom: 0
before dom: 0x7fff5fbff3e0
before ldom: 0
===
after received 0x7fff5fbff3e0
after fom: 0
after dom: 0x7fff5fbff3e0
after ldom: 0
===
我的问题是:为什么ѭ2失败? 并且,在我想像例子一样维护类结构的前提下,继续使用共享库:如何从
Base*
成功获取
Derived<some type>*
?     

解决方法

我假设您使用的是Linux / GCC,因为在Windows上它应该“正常工作”。 它不能与GCC“配合使用”,因为在性能上,GCC中的RTTI支持依赖于指针比较。在此GCC常见问题解答中对所有内容进行了说明,包括如何解决。编辑:虽然,此常见问题解答说,它不适用于ѭ18work,而与共享库的显式链接应该起作用;所以也许还有其他事情,例如下面提到的错误。 我发现其他一些链接可能会有所帮助: dynamic_cast从lt_dlopen(libtool)加载的共享库中的接口不起作用 带接口的动态转换 Mac OS 10.6 Snow Leopard中的C ++ dynamic_cast错误     ,这并不奇怪。即使对于普通的非模板类,也永远不要期望RTTI跨共享库边界工作。对于某些编译器,在某些操作系统上,带有某些编译器或链接器选项,它可能会起作用,但通常,它不会,也不需要(在标准中未明确说明)。即使使它起作用,从长远来看,它也是不可持续的。 以我的经验,RTTI无法在共享库边界之间交叉的情况远远超过了可以的情况。 解决方案是: 将这些派生类型中所有对象的构造限制在使用dynamic_cast的共享库代码中(此解决方案很难管理)。 完全不要使用dynamic_cast(此解决方案是理想的,很少适用)。 不要使用共享库(请评估共享库是否确实是您所需要的,或者从共享库公开更高级别的接口,该接口不公开要派生的多态类型(这似乎表明\“开放式架构\”更适合您的应用程序))。 定义自己的RTTI系统和强制转换运算符(这可能很难,具体取决于您的技能,但这并不需要太多代码,许多主流项目都使用此解决方案,并且您可以找到很多有关如何执行此操作的示例这个)。