没有针对全专业模板类的离线虚拟方法定义

时间:2018-07-10 10:35:40

标签: c++ c++11 templates clang

我有这种结构:

静态库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将在每个翻译单元中发出”

为什么这个警告?我没有任何纯虚拟析构函数,并且在基类中提供了默认的析构函数。以及因为使用模板,如何避免使用离线虚拟方法?

2 个答案:

答案 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
{};

不过,还有更多避免它的方法。您可以尝试找到更适合您的方法。