[英]Default move construction
《 C ++编程语言 》一书提供了有关默认操作的示例。 我的问题集中在默认移动操作上。
struct S {
std::string a;
int b;
};
S f(S arg) {
S s0{};
S s1(s0); //s1 {s0}; in the book
s1 = arg;
return s1;
}
之后它说:
s1的副本结构复制了s0.a和s0.b。 s1的返回将移动s1.a和s1.b,将s1.a保留为空字符串,而s1.b保持不变。 请注意,内置类型的移出对象的值未更改。 那是编译器要做的最简单,最快的事情。
我认为这意味着如果我写:
S s3{"tool",42};
f(s3);
由于s1的值已移动,因此s1.a将返回到“”,而s1.b保持不变? 然后,当f()完成执行时,s1将被销毁吗? 我正在尝试找到一种方法来测试我的猜测,但是在函数执行后我无法找到一种方法来了解s1的值,因为它当然是局部的。 我写了一个析构函数,只是为了在值被销毁之前找到它们。
~S() {
std::cout << a << " " << b << '\n';
}
输出为:
0 //values of s0?
tool 42
tool 42
tool 42
看来我的猜测是完全错误的,我完全不理解本文。 谁能解释引号中的文字?
s1.a
将返回到""
也许。 根据move构造函数的规范,它“处于未指定值的有效状态”。 通常,如果字符串使用动态分配的数组,并且两个字符串都使用兼容的分配器,则此举将转移该数组的所有权,而将旧字符串保留为空。 但是,如果字符串使用“短字符串优化”,即在string
对象本身内部存储短字符串,以节省动态内存分配的成本,则保留旧字符串不变的速度会更快。
并且
s1.b
不变
是。 从中分配原始类型时,永远不会对其进行修改。
然后,当
f()
完成执行时,s1
将被销毁吗?
可能不是。 除非您有非常原始的编译器或故意禁用优化,否则可能在此处使用移动省略的优化(有时称为“返回值优化”)。 s1
将在调用者的堆栈框架中创建,因此不需要任何工作即可将其返回。
函数执行后,我找不到找到s1值的方法
不,返回后无法检查对象,因为它不再存在。 如果要查看移动的效果,可以移动到新的局部变量
S s2 = std::move(s1);
然后检查s1
。 或者您可以编写自己的move构造函数
S(S && other) : a(std::move(other.a)), b(b) {
std::cout << other.a << '\n';
}
但是,如上所述,这可能不会用于函数返回值。
首先,通过定义析构函数,您可以禁用隐式move构造函数。 您必须添加到您的代码:
S(const S&) = default;
S(S&&) = default;
S& operator=(const S&) = default;
S& operator=(S&&) = default; // not required here but should be added for completeness
然后,无论如何,RVO发挥了作用。 如其他答案所述,允许编译器取消对复制和移动构造函数的调用。 在GCC和clang中,可以通过添加-fno-elide-constructors
编译器选项来禁用此功能。 之后,您将获得以下输出:
42 // moved s1 (this can theoretically be different, because the value of s1.a is unspecified)
0 // s0
tool 42 // arg
tool 42 // return value of f()
tool 42 // s3
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.