C++ 对于完整的专用模板类,没有越界的虚拟方法定义

C++ 对于完整的专用模板类,没有越界的虚拟方法定义,c++,c++11,templates,clang,C++,C++11,Templates,Clang,我有这样的结构: 静态库A 接口.h interface.cpp 使用A的静态库B BaseT.h #包括“interface.h” 模板 类BaseT final:公共接口 { static_断言(false,“不能使用此专门化”); }; 专门化1.h #包括“baset.h” 使用MyType=BaseT; 模板 类BaseT:公共接口 { 公众: BaseT()=默认值; void pureMethod1()final{} void pureMethod2()final{} }; 专

我有这样的结构:

静态库A

接口.h

interface.cpp

使用A的静态库B

BaseT.h

#包括“interface.h”
模板
类BaseT final:公共接口
{
static_断言(false,“不能使用此专门化”);
};
专门化1.h

#包括“baset.h”
使用MyType=BaseT;
模板
类BaseT:公共接口
{
公众:
BaseT()=默认值;
void pureMethod1()final{}
void pureMethod2()final{}
};
专门的2.h

#包括“baset.h”
使用MyType=BaseT;
模板
类BaseT:公共接口
{
公众:
BaseT()=默认值;
void pureMethod1()final{}
void pureMethod2()final{}
};
在两个完全专业化的课程中,我都收到了来自clang的警告:

警告:“BaseT没有越界虚拟方法定义:它的vtable将在每个转换单元中发出”


为什么要发出这样的警告?我没有任何纯虚拟析构函数,基类中提供了一个默认析构函数。由于我使用的是模板,如何避免使用越界虚拟方法?

虚拟函数的重新着色通常通过使用函数指针表vtable来实现。实现接口(即从接口继承)的每个类都有这样一个表,其中条目指向其虚拟函数的实现。此表必须位于编译期间生成的至少一个目标文件中,默认情况下,许多编译器将其放入包含类中第一个虚拟函数实现的目标文件中

在您的例子中,
BaseT
的专门化中的所有虚函数都是在类声明中内联定义的。在这种情况下,没有唯一的对象文件来放置它们的实现,它们将包含在使用它们的所有对象文件中。这反过来意味着将vtable与第一个虚拟函数的实现放在一起的方法将不再有效。这使得编译器后退,向所有对象文件添加vtable的一个版本,只是为了安全起见,并且为了让程序员意识到这一点,它会发出警告


这不是一个真正的问题,因为链接器将从一个目标文件中选择vtable,并包含在最终的二进制文件中。

虚拟函数的重新着色通常通过使用函数指针表vtable来实现。实现接口(即从接口继承)的每个类都有这样一个表,其中条目指向其虚拟函数的实现。此表必须位于编译期间生成的至少一个目标文件中,默认情况下,许多编译器将其放入包含类中第一个虚拟函数实现的目标文件中

在您的例子中,
BaseT
的专门化中的所有虚函数都是在类声明中内联定义的。在这种情况下,没有唯一的对象文件来放置它们的实现,它们将包含在使用它们的所有对象文件中。这反过来意味着将vtable与第一个虚拟函数的实现放在一起的方法将不再有效。这使得编译器后退,向所有对象文件添加vtable的一个版本,只是为了安全起见,并且为了让程序员意识到这一点,它会发出警告


这不是一个真正的问题,因为链接器将从一个目标文件中选择vtable并包含在最终二进制文件中。

出现此警告,因为您的虚拟函数实现位于头文件中

头文件只是用
#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
{};
class ImplForCustomClass2:公共接口
{
公众:
虚空pureMethod1()最终覆盖;
虚空pureMethod2()最终覆盖;
};
模板
类库:public ImplForCustomClass2
{};

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

出现此警告,因为您的虚拟函数实现位于头文件中

头文件只是用
#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
{};
class ImplForCustomClass2:公共接口
{
公众:
虚空pureMethod1()最终覆盖;
虚空pureMethod2()最终覆盖;
};
模板
类库:public ImplForCustomClass2
{};

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

您应该简化此代码以生成mcve。由于静态_断言失败和其他问题,它现在无法编译。@Passenger这是另一个问题question@V.Kravchenko是的,我错了,没有通读。唯一的问题是所有的对象文件都会稍微大一点。可执行文件中仍然只有一个vtable。链接器会处理这个问题(但可能需要更长的时间来排序)
#include "interface.h"
template<class T>
class BaseT final : public Interface
{
   static_assert(false, "can't use this specialization");
};
#include "baset.h"

using MyType = BaseT<CustomClass1>;

template<>
class BaseT<CustomClass1> : public Interface
{
public:
   BaseT() = default;

   void pureMethod1() final {}
   void pureMethod2() final {}
};
#include "baset.h"

using MyType = BaseT<CustomClass2>;

template<>
class BaseT<CustomClass2> : public Interface
{
public:
   BaseT() = default;

   void pureMethod1() final {}
   void pureMethod2() final {}
};
class ImplForCustomClass2 : public Interface
{
public:
    virtual void pureMethod1() final override;
    virtual void pureMethod2() final override;
};
template<>
class Base<CustomClass2> : public ImplForCustomClass2
{};