我有这种结构:
静态库A
interface.h
class Interface
{
public:
virtual ~Interface() // no pure virtual dtor
virtual void pureMethod1() = 0;
virtual void pureMethod2() = 0;
virtual void virtualMethod1();
virtual void virtualMethod2();
};
interface.cpp
include "interface.h"
Interface::~Interface() = default;
Interface::virtualMethod1() {}
Interface::virtualMethod2() {}
使用A的静态库B
BaseT.h
#include "interface.h"
template<class T>
class BaseT final : public Interface
{
static_assert(false, "can't use this specialization");
};
specialized1.h
#include "baset.h"
using MyType = BaseT<CustomClass1>;
template<>
class BaseT<CustomClass1> : public Interface
{
public:
BaseT() = default;
void pureMethod1() final {}
void pureMethod2() final {}
};
specialized2.h
#include "baset.h"
using MyType = BaseT<CustomClass2>;
template<>
class BaseT<CustomClass2> : public Interface
{
public:
BaseT() = default;
void pureMethod1() final {}
void pureMethod2() final {}
};
在两个完全专业的课程中,我都收到了来自clang的警告:
警告:“ BaseT没有离线虚拟方法定义:其vtable将在每个翻译单元中发出”
为什么这个警告?我没有任何纯虚拟析构函数,并且在基类中提供了默认的析构函数。以及因为使用模板,如何避免使用离线虚拟方法?
答案 0 :(得分:4)
虚函数的重排通常通过使用函数指针表vtable来实现。每个实现该接口(即从其继承)的类都有一个这样的表,其中的条目指向其对虚拟功能的实现。该表必须位于编译期间生成的至少一个目标文件中,默认情况下,许多编译器将其放在包含该类中第一个虚函数实现的目标文件中。
在您的情况下,BaseT
专业领域中的所有虚函数均在类声明中内联定义。在这种情况下,没有可放入其实现的唯一目标文件,它们将包含在使用它的所有目标文件中。这反过来意味着将vtable与第一个虚拟函数的实现放在一起的方法将不再起作用。这样一来,编译器便退回去向所有目标文件添加vtable版本,以确保安全,并使程序员意识到这一点,从而发出警告。
这并不是真正的问题,因为链接器将从目标文件之一中选择vtable,并将其包含在最终的二进制文件中。
答案 1 :(得分:3)
发生此警告,因为您的虚拟功能实现位于头文件中。
仅使用#include
指令将头文件粘贴到ccp文件中。因此,您的文件将被粘贴到多个cpp文件中。并且由于在类内部提供了实现,因此每个cpp文件都是独立的。因此,将为每个目标文件生成所有实例化
没有简单的方法可以避免这种情况,但是有一些可能的解决方案。简单的解决方案是找到使用该实现的单个cpp。 另一个解决方法是在实现这些方法的地方删除模板,即
class ImplForCustomClass2 : public Interface
{
public:
virtual void pureMethod1() final override;
virtual void pureMethod2() final override;
};
template<>
class Base<CustomClass2> : public ImplForCustomClass2
{};
不过,还有更多避免它的方法。您可以尝试找到更适合您的方法。