繁体   English   中英

默认移动构造

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM