我在头文件( obj.h
)中使用显式自动移动构造函数( = default
)声明了 template<bool VAR> struct Obj
模板 .
// obj.h
#pragma once
#include <vector>
template<bool VAR>
struct Obj {
std::vector<int> member;
Obj(int m): member(m) { }
Obj(Obj&&) = default;
int member_fun() const;
};
extern template struct Obj<false>;
extern template struct Obj<true>;
模板的成员函数在另一个文件( obj.cpp
)中定义,并显式实例化模板:
// obj.cpp
#include "obj.h"
template<bool VAR>
int Obj<VAR>::member_fun() const {
return 42;
}
template struct Obj<false>;
template struct Obj<true>;
然后从主文件( main.cpp
)使用此模板:
// main.cpp
#include <utility>
#include "obj.h"
int main() {
Obj<true> o1(20);
Obj<true> o2(std::move(o1));
return o2.member_fun();
}
然后编译 .cpp
并使用以下 Makefile
链接在一起:
#CXX=clang++
CXX=g++
CXXFLAGS=-Wall -Wextra -std=c++14
a.out: obj.o main.o
$(CXX) $(CXXFLAGS) $^ -o a.out
obj.o: obj.cpp obj.h
$(CXX) $(CXXFLAGS) -c $< -o $@
main.o: main.cpp obj.h
$(CXX) $(CXXFLAGS) -c $< -o $@
但是,我收到一个链接器错误: undefined reference to 'Obj<true>::Obj(Obj<true>&&)'
- 编译器显然没有实例化构造函数 .
-
Obj<true>::member_fun()
已定义,如果我从main.cpp
删除对移动构造函数的引用,程序确实链接成功 . -
如果我从 Headers 中删除
extern template
,程序将编译 . -
如果我使用
int
而不是std::vector<int>
作为member
的类型,程序也会编译 . -
cppreference.com声称“编译器会将移动构造函数声明为其类的非显式内联公共成员” . 但是,手动定义的
Obj(int)
构造函数也是内联的,但它已正确实例化 .
(我在一个用GCC编译得很好的项目中收到了Clang这个错误,所以我认为这是一个Clang错误 . 但是,当我把问题简化为这个简单的情况时,GCC 5.4.0和Clang 3.8.0都产生相同的错误结果) .
3 回答
有趣 . 我认为你的代码是正确的,因为:
你的默认移动构造函数是隐含的
inline
,因为:[dcl.fct.def.default]/5:
和[class.mfct]/1:
因此根据[temp.explicit]/10(强调我的)免除显式模板实例化:
实际上,如果您尝试除
-O0
之外的任何优化模式,问题就会消失 .-O0
是一种特殊模式,其中内联函数未内联 . 但是没关系,编译器必须在这种情况下生成inline
默认的移动构造函数,就像它与其他构造函数一样 .所以对我来说这看起来像编译器错误 . 另请查看LLVM#22763和GCC#60796 .
我看到至少2种可能的解决方法:
Solution 1
现在不要使用
extern template ...
(编译时间会受到影响,但没有什么大不了的) .Solution 2
欺骗编译器在
obj.cpp
中生成抑制的构造函数这个只用于
-O0
模式 . 在 生产环境 代码中,将使用内联版本 .互联网上没有关于这个主题的很多信息 . 虽然看一些消息来源,但我会得出以下结论:
extern template
应该阻止隐式实例化,尽管在所有示例中,explicit
实例化都没有此extern template
定义 .从我可以阅读的内容,特别是关于提案和GCC邮件列表(参见下面的参考资料),
extern template
并不阻止implicit
实例化,尽管模板的所有实例化 . 这将包括您的explicit
实例化 .从这里,我得出结论,你应该删除你想要显式实例化的翻译单元中的
extern template
.参考文献:
SO: Using extern templace C++11
Stroustrup: C++11FAQ
Standard proposal
CppReference: Class Template
GCC Mailing list: extern template declaration accepted after explicit instantiation
您可能遇到编译器错误 .
见https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60796 .
我在CLang上遇到了类似的行为,但找不到错误报告 .