[已编辑,以显示.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上发现的每个类似问题都是针对没有离线虚拟方法的类,因此我一直无法找到答案。
答案 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报告者的警告,在这种情况下,该警告似乎是虚假的。
尽管对错误报告的最后评论称其为已修复,但该错误仍以“新”状态列出,因此我不认为它已修复。