问题描述
我有一个模板< bool VAR>在头文件(
obj.h
)中使用显式自动移动构造函数( = default $)声明的struct Obj
模板
I have a template<bool VAR> struct Obj
template declared in a header file (obj.h
) with explicit automatic move constructor (= default
).
// 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
),其中包含模板的显式实例:
The member function of the template is defined in another file (obj.cpp
) with explicit instantiation of the template:
// 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
):
This template is then used from the main file (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
然后将s与以下 Makefile
链接在一起:
The .cpp
s are then compiled and linked together with the following 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 $@
但是,出现链接器错误:未定义的引用'Obj< true> :: Obj(Obj< true&&))'
-显然编译器没有实例化构造函数。
However, I get a linker error: undefined reference to 'Obj<true>::Obj(Obj<true>&&)'
-- the compiler apparently did not instantiate the constructor.
-
Obj< true> :: member_fun()
已定义且程序确实如果我从main.cpp
删除对move构造函数的引用,则成功链接。 - 如果我删除
extern模板
从头开始,程序将进行编译。 - 如果我使用
int
而不是std :: vector< int< int>
表示成员
的类型,程序也会编译。 - 声称编译器会将移动构造函数声明为非-显式 inline 该类的公共成员。但是,手动定义的
Obj(int)
构造函数也是内联的,但已正确实例化。
Obj<true>::member_fun()
is defined and the program indeed links successfully if I remove the reference to the move constructor frommain.cpp
.- If I remove
extern template
from the header, the program compiles. - If I use
int
instead ofstd::vector<int>
for the type ofmember
, the program also compiles. - cppreference.com claims that "the compiler will declare a move constructor as a non-explicit inline public member of its class". However, the manually defined
Obj(int)
constructor is also inline, but it is correctly instantiated.
(我在一个用GCC编译良好的项目中收到Clang的错误,所以我认为这是Clang的错误。但是,当我将问题简化为这种简单情况时,GCC 5.4.0和Clang 3.8.0会产生相同的结果。)
(I received this error with Clang in a project that compiled fine with GCC, so I thought this was a Clang bug. However, when I reduced the problem to this simple case, both GCC 5.4.0 and Clang 3.8.0 produce the same results.)
推荐答案
有趣。我认为您的代码是正确的,因为:
Interesting. I think your code is correct, because:
您的默认移动构造函数隐式地 inline
,原因是:
Your defaulted move-constructor is implicitly inline
because of:
:
a href = http://eel.is/c++draft/class.mfct#1 rel = nofollow> [class.mfct] / 1 :
And [class.mfct]/1:
因此,根据(重点是我的):
And thus is exempt from explicit template instantiation according to [temp.explicit]/10 (emphasis mine):
实际上,如果您尝试除 -O0 $ c以外的任何优化模式$ c>,问题消失了。
In fact, if you try any optimization mode other than -O0
, the problem disappears.
-O0
是一种特殊模式,其中不对内联函数进行内联。但这没关系,在这种情况下,编译器必须生成 inline
默认的移动构造函数,就像与其他构造函数一样。
-O0
is a special mode in which inlined functions are not inlined. But it shouldn't matter, the compiler must in that case generate the inline
defaulted move-constructor like it does with the other constructor.
对我来说,这看起来像是编译器错误。还要查看和。
So to me this looks like a compiler bug. Also look at LLVM#22763 and GCC#60796.
我认为至少有2种可能解决方法:
I see at least 2 possible workarounds:
解决方案1
请勿使用 extern模板...
目前(编译时间会受到影响,但除此之外没什么大不了的。)
Do not use extern template ...
for now (compilation times will suffer but otherwise no big deal).
解决方案2
欺骗编译器在 obj.cpp
template<> Obj<true>::Obj(Obj&&) noexcept = default;
该选项仅用于 -O0
模式。在生产代码中,将使用内联版本。
This one will only be used in -O0
mode. In production code the inlined version will be used instead.
这篇关于显式实例化的类模板中的自动构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!