我有几个类需要检查是否生成了默认移动构造函数。是否有一种方法可以检查这一点(无论是编译时断言,还是解析生成的目标文件,或其他方法)?
激励性例子:
class MyStruct : public ComplicatedBaseClass {
std::vector<std::string> foo; // possibly huge
ComplicatedSubObject bar;
};
如果任何基类成员或
Complicated...Object
类的任何成员无法移动,MyStruct
将不会生成其隐式移动构造函数,并且在可以进行移动的情况下,可能无法优化复制foo
的工作,即使foo
是可移动的。请注意保留HTML标签。
我希望避免以下情况:
- 繁琐地检查隐式移动构造函数生成的条件,
- 显式且递归地默认所有受影响类、它们的基类和成员的特殊成员函数,只为了确保有一个移动构造函数可用。
我已经尝试过以下方法但它们不起作用:
- 显式使用
std::move
—— 如果没有移动构造函数,则会调用复制构造函数。 - 使用
std::is_move_constructible
—— 只要没有显式删除移动构造函数,就会成功,因为会默认生成一个接受const Type&
的复制构造函数 (至少)。 - 使用
nm -C
检查是否存在移动构造函数 (见下文)。然而,还有一种替代方法可行 (请参见答案)。
我尝试查看像这样的简单类生成的符号: ```
```
#include <utility>
struct MyStruct {
MyStruct(int x) : x(x) {}
//MyStruct(const MyStruct& rhs) : x(rhs.x) {}
//MyStruct(MyStruct&& rhs) : x(rhs.x) {}
int x;
};
int main() {
MyStruct s1(4);
MyStruct s2(s1);
MyStruct s3(std::move(s1));
return s1.x + s2.x + s3.x; // Make sure nothing is optimized away
}
生成的符号看起来像这样:
$ CXXFLAGS="-std=gnu++11 -O0" make -B x; ./x; echo $?; nm -C x | grep MyStruct | cut -d' ' -f3,4,5
g++ -std=gnu++11 -O0 x.cc -o x
12
.pdata$_ZN8MyStructC1Ei
.pdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.text$_ZN8MyStructC1Ei
.text$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.xdata$_ZN8MyStructC1Ei
.xdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
MyStruct::MyStruct(int)
std::remove_reference<MyStruct&>::type&&
当我显式默认复制和移动构造函数时(无符号),输出相同。
使用自己的复制和移动构造函数,输出如下:
$ vim x.cc; CXXFLAGS="-std=gnu++11 -O0" make -B x; ./x; echo $?; nm -C x | grep MyStruct | cut -d' ' -f3,4,5
g++ -std=gnu++11 -O0 x.cc -o x
12
.pdata$_ZN8MyStructC1Ei
.pdata$_ZN8MyStructC1EOKS_
.pdata$_ZN8MyStructC1ERKS_
.pdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.text$_ZN8MyStructC1Ei
.text$_ZN8MyStructC1EOKS_
.text$_ZN8MyStructC1ERKS_
.text$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.xdata$_ZN8MyStructC1Ei
.xdata$_ZN8MyStructC1EOKS_
.xdata$_ZN8MyStructC1ERKS_
.xdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
MyStruct::MyStruct(int)
MyStruct::MyStruct(MyStruct&&)
MyStruct::MyStruct(MyStruct const&)
std::remove_reference<MyStruct&>::type&& std::move<MyStruct&>(MyStruct&)
所以看起来这种方法也不起作用。
然而,如果目标类具有显式移动构造函数的成员,则隐式生成的移动构造函数将对目标类可见。也就是说,使用以下代码时:
#include <utility>
struct Foobar {
Foobar() = default;
Foobar(const Foobar&) = default;
Foobar(Foobar&&) {}
};
struct MyStruct {
MyStruct(int x) : x(x) {}
int x;
Foobar f;
};
int main() {
MyStruct s1(4);
MyStruct s2(s1);
MyStruct s3(std::move(s1));
return s1.x + s2.x + s3.x; // Make sure nothing is optimized away
}
我将获取MyStruct
的移动构造函数符号,但不是复制构造函数,因为它似乎完全是隐式的。我推测编译器如果可以生成一个平凡内联的移动构造函数,否则会生成一个非平凡的移动构造函数来调用其他非平凡的移动构造函数。然而这仍然不能帮助我完成我的任务。
template<class T> T const& copy(T const& t){return t;}
,然后MyStruct s2(copy(s1));
。然后复制构造函数可能会出现在你的转储中。 - Yakk - Adam NevraumontMyStruct
中。我猜这与构造函数是否为平凡的有关。请注意,任何优化级别都不会导致任何构造函数符号--可能是由于内联。等等,-fno-inline
? - Irfyis_nothrow_move_constructible
? - M.M