我正在努力学习移动语义,以便将它介绍给我的学生。我一直在使用高度简化的矢量或类似字符串的类来管理内存,其成员输出消息来演示它们的活动。我正在尝试开发一组简单的例子来向学生展示。
RVO和其他地方的gcc 4.7和clang的建筑省略积极地消除了复制和移动施工,所以虽然我可以很容易地看到工作中的移动分配,但我唯一一次看到移动施工在工作中是因为我关闭了建筑省略在gcc 4.7中使用-fno-elide-constructors。
显式复制构造声明
MyString newString(oldString);
即使启用了elision,也会调用复制构造函数。但是像
这样的东西MyString newString(oldString1 + oldString2);
由于省略,不会调用移动构造函数。
明确使用std :: move的任何内容都不会成为一个简单的例子,因为解释std :: move必须稍后。
所以我的问题:是否有一个简单的代码示例,即使复制/移动构造函数被删除,也会调用移动构造?
答案 0 :(得分:7)
简单示例将是返回的函数的参数。该标准明确禁止在这种情况下忽略此举(不是他们可以......):
std::vector<int> multiply( std::vector<int> input, int value ) {
for (auto& i : input )
i *= value;
return input;
}
此外,您可以明确地请求移动构造以获得更简单的结构,尽管有一些更人为的例子:
T a;
T b( std::move(a) );
嗯......还有一个不涉及std::move
的问题(技术上可以省略,但大多数编译器可能不会):
std::vector<int> create( bool large ) {
std::vector<int> v1 = f();
std::vector<int> v2 = v1; // modify both v1 and v2 somehow
v2.resize( v2.size()/2 );
if ( large ) {
return v1;
} else {
return v2;
}
}
虽然优化器可以通过将代码重写为:
来忽略它std::vector<int> create( bool large ) {
if ( large ) {
std::vector<int> v1 = f();
std::vector<int> v2 = v1; // modify both v1 and v2 somehow
v2.resize( v2.size()/2 );
return v1;
} else {
std::vector<int> v1 = f();
std::vector<int> v2 = v1; // modify both v1 and v2 somehow
v2.resize( v2.size()/2 );
return v2;
}
}
我非常怀疑编译器实际上会这样做。请注意,在每个代码路径中,创建在v1
和v2
之前已知的对象,因此优化器可以在重写后在返回位置找到正确的对象。
12.8 / 31中描述了可以省略复制/移动的情况。如果您设法编写不属于这些类别且类型具有移动构造函数的代码,则将调用移动构造函数。
答案 1 :(得分:1)
MyString newString(oldString)
是副本。这里没有什么可以忽视的;我们最终得到了两个对象。
MyString newString(oldString1 + oldString2);
副本,因此可以省略副本,并且就地直接构建连接。
这是一个非常便宜的不可移动的移动建筑的例子:
MyString boo()
{
MyString s("Hello");
return std::move(s); // move-construction from the local "s", never elided
}
显式广播使得s
不符合RVO的条件,因此返回值将从s
移动构建。