简体   繁体   English

默认移动构造

[英]Default move construction

The book The C++ Programming Language has this example about default operations. C ++编程语言 》一书提供了有关默认操作的示例。 My question focuses on default move operation. 我的问题集中在默认移动操作上。

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;
}

After that it says: 之后它说:

The copy construction of s1 copies s0.a and s0.b. s1的副本结构复制了s0.a和s0.b。 The return of s1 moves s1.a and s1.b, leaving s1.a as the empty string and s1.b unchanged. s1的返回将移动s1.a和s1.b,将s1.a保留为空字符串,而s1.b保持不变。 Note that the value of a moved-from object of a built-in type is unchanged. 请注意,内置类型的移出对象的值未更改。 That's the simplest and fastest thing for the compiler to do. 那是编译器要做的最简单,最快的事情。

I think this means if I write: 我认为这意味着如果我写:

S s3{"tool",42};
f(s3);

Since the value of s1 is moved, s1.a will go back to "" and s1.b is unchanged? 由于s1的值已移动,因此s1.a将返回到“”,而s1.b保持不变? Then when f() finishes executing, s1 will be destroyed? 然后,当f()完成执行时,s1将被销毁吗? I am trying to find a way to test my guess but I can't find a way to know the values of s1 after the function executes because it's of course local. 我正在尝试找到一种方法来测试我的猜测,但是在函数执行后我无法找到一种方法来了解s1的值,因为它当然是局部的。 I wrote a destructor just to find the values before they get destroyed. 我写了一个析构函数,只是为了在值被销毁之前找到它们。

~S() {
    std::cout << a << " " << b << '\n';
}

The outputs are: 输出为:

0 //values of s0?
tool 42
tool 42
tool 42

It seems my guess is totally wrong and I totally don't understand the text. 看来我的猜测是完全错误的,我完全不理解本文。 Can anyone explain the text in the quote clearer? 谁能解释引号中的文字?

s1.a will go back to "" s1.a将返回到""

Maybe. 也许。 From the specification of the move constructor, it "is left in a valid state with an unspecified value." 根据move构造函数的规范,它“处于未指定值的有效状态”。 Typically, if the string is using a dynamically allocated array, and both strings use compatible allocators, then the move will transfer ownership of the array, leaving the old string empty. 通常,如果字符串使用动态分配的数组,并且两个字符串都使用兼容的分配器,则此举将转移该数组的所有权,而将旧字符串保留为空。 But if the string uses "short string optimisation", where short strings are stored inside the string object itself to save the cost of dynamic memory allocation, then it would be faster to leave the old string unchanged. 但是,如果字符串使用“短字符串优化”,即在string对象本身内部存储短字符串,以节省动态内存分配的成本,则保留旧字符串不变的速度会更快。

and s1.b is unchanged 并且s1.b不变

Yes. 是。 Primitive types are never modified when assigned from. 从中分配原始类型时,永远不会对其进行修改。

Then when f() finishes executing, s1 will be destroyed? 然后,当f()完成执行时, s1将被销毁吗?

Probably not. 可能不是。 The optimisation of move elision (sometimes called "return value optimisation") is probably used here, unless you have a very primitive compiler or deliberately disable the optimisation. 除非您有非常原始的编译器或故意禁用优化,否则可能在此处使用移动省略的优化(有时称为“返回值优化”)。 s1 will be created in the caller's stack frame, so that no work is needed to return it. s1将在调用者的堆栈框架中创建,因此不需要任何工作即可将其返回。

I can't find a way to know the values of s1 after the function executes 函数执行后,我找不到找到s1值的方法

No, there's no way to examine the object after the return, since it no longer exists. 不,返回后无法检查对象,因为它不再存在。 If you want to see the effect of moving, you could move to a new local variable 如果要查看移动的效果,可以移动到新的局部变量

S s2 = std::move(s1);

and examine s1 afterwards. 然后检查s1 Or you could write your own move constructor 或者您可以编写自己的move构造函数

S(S && other) : a(std::move(other.a)), b(b) {
    std::cout << other.a << '\n';
}

but, as noted above, this probably won't be used for the function return value. 但是,如上所述,这可能不会用于函数返回值。

First of all, by defining destructor you disabled the implicit move constructor. 首先,通过定义析构函数,您可以禁用隐式move构造函数。 You have to add to your code: 您必须添加到您的代码:

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

Then, anyway RVO comes into play. 然后,无论如何,RVO发挥了作用。 As noted in other answers, the compiler is allowed to elide calls to copy and move constructors. 如其他答案所述,允许编译器取消对复制和移动构造函数的调用。 In GCC and clang you can disable this by adding -fno-elide-constructors compiler option. 在GCC和clang中,可以通过添加-fno-elide-constructors编译器选项来禁用此功能。 After that you'll get this output: 之后,您将获得以下输出:

 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

Demo 演示版

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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