当存在离线虚拟时,为什么显式模板实例化会导致弱模板vtables警告?

时间:2019-05-08 13:24:30

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

[已编辑,以显示.cpp和hpp之间的拆分]

// file.hpp
class Base {
 public:
    virtual ~Base(void);
    Base(void);
    Base(const Base&) = default;
};

template<typename T>
class Derived: public Base {
 public:
    Derived(void);
    bool func(void);
};
// file.cpp
#include "file.hpp"

Base::~Base(void) {}
Base::Base(void) {}

template<typename T>
bool Derived<T>::func(void) {return true;}

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

// required to avoid linker errors when using `Derived` elsewhere
template class Derived<int>;

最后一行在Clang v8.0 warning: explicit template instantiation 'Derived<int>' will emit a vtable in every translation unit [-Wweak-template-vtables]

中导致以下编译器警告

我的理解是,由于Base至少具有一种离线虚拟方法,因此此处所有类的vtable都将锚定到此转换单元,因此this guidance在LLVM编码标准中。 那为什么会生成此警告?

在此处使用我使用的特定编译器版本查看Godbolt:https://godbolt.org/z/Kus4bq

我在SO上发现的每个类似问题都是针对没有离线虚拟方法的类,因此我一直无法找到答案。

2 个答案:

答案 0 :(得分:2)

template class Derived<int>;是否存在于头文件中,该头文件又包含在多个源文件中?

在这种情况下,类Derived<int>的vtable和方法将存在于多个目标文件中。链接器必须弄清楚如何处理这些多个副本。

我不确定如何根据c ++标准解决编译器和链接器的问题。但是通常我不在乎,因为这些副本通常看起来应该一样。

但是要避免此问题,您应该将extern template class Derived<int>;放在头文件中,并将template class Derived<int>;放在恰好1个编译单元(也就是源文件)中

编辑(以反映将代码拆分为“ file.hpp”和“ file.cpp”):

我玩过clang-6(我是我拥有的最新版本)

对我来说,警告的类型为“如果执行X,则将发生Y”。但这并不意味着y已经发生。

在这种情况下,Y是多个vtable,只有将template class Derived<int>;放在多个源文件中才会发生,而不会这样做。

您的来源中的每个template class Derived<int>;都会触发警告,因此,如果您仅看到一个警告,则只会有一个vtable。

但是有一种摆脱警告的方法:没有显式的实例化,而是依靠编译器隐式实例化该类。

为此,您必须将所有模板定义都放在头文件中。因此,移动定义:

template<typename T>
bool Derived<T>::func(void) {return true;}

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

进入头文件,然后删除extern template class Derived<int>;template class Derived<int>;

答案 1 :(得分:0)

我倾向于认为这是由于Clang中的错误所致,此处报道:https://bugs.llvm.org/show_bug.cgi?id=18733

在链接断开的情况下在此处重新发布内容:

  

RafaelÁviladeEspíndola2014-02-05 00:00:19 PST

     

给予

template<typename T>
class foo {
  virtual ~foo() {}
};

extern template class foo<int>;
template class foo<int>;
  

c警告:

test.cpp:6:23: warning: explicit template instantiation 'foo<int>' will emit a vtable in every translation unit [-Wweak-template-vtables]
extern template class foo<int>;
                      ^
1 warning generated.
  

请注意,警告指向显式模板实例化声明,但由定义触发。这可能应该检查定义是否在标题中。在.cpp文件中,只有一个翻译单位可以看到它。

     

评论1 David Faure 2016-02-13 12:21:27 PST

     

是的,这种误报确实令人讨厌。感谢您举报。 lang开发人员:感谢您修复它:-)

对于其他人的意见,我将不胜感激,但我同意bug报告者的警告,在这种情况下,该警告似乎是虚假的。

尽管对错误报告的最后评论称其为已修复,但该错误仍以“新”状态列出,因此我不认为它已修复。