简体   繁体   English

在C ++中,返回时使用移动操作意味着什么?

[英]In C++, what does it mean to use a move operation on return?

I'm reading through Bjarne Stroustrup's The C++ Programming Language (4th edition) and on p. 我正在阅读Bjarne Stroustrup的The C ++ Programming Language (第4版)和p。 516 he says: 516他说:

How does the compiler know when it can use a move operation rather than a copy operation? 编译器如何知道何时可以使用移动操作而不是复制操作? In a few cases, such as for a return value, the language rules say that it can (because the next action is defined to destroy the element) 在少数情况下,例如对于返回值,语言规则说它可以(因为下一个动作被定义为销毁元素)

Also on p. 也在p。 517 he says: 517他说:

[The object] has a move constructor so that "return by value" is simple and effecient as well as "natural" [对象]有一个移动构造函数,因此“按值返回”既简单又有效,而且“自然”

If return always uses a move operation, then why doesn't something like the following work? 如果return总是使用移动操作,那为什么不能像以下那样工作呢?

#include <vector>
#include <assert.h>

using namespace std;

vector<int> ident(vector<int>& v) {
    return v;
};

int main() {
    vector<int> a {};
    const vector<int>& b = ident(a);
    a.push_back(1);
    assert(a.size() == b.size());
}

Shouldn't a and b be be pointing to the same objects? ab不应该指向相同的对象吗?

vector<int> foo() {
    return ...;
};

The function returns by value. 该函数按值返回。 So the returned object is a newly created object. 因此返回的对象是新创建的对象。 This is true regardless of how the object is created: via copy construction, move construction, default construction, or any kind of construction. 无论对象是如何创建的,都是如此:通过复制构造,移动构造,默认构造或任何类型的构造。

The matter of copy/move construction is mostly a matter of efficiency. 复制/移动建设的问题主要是效率问题。 If the object you create from is used after then you can't do anything but copy it. 如果在此之后使用您创建的对象,则除了复制它之外您无法执行任何操作。 But if you know the object you create from is not used anymore after (like is the case with prvalues or with an object in a simple return statement) then you can move from it because move usually steals from the object moved from. 但是如果你知道你创建的对象之后不再使用(就像prvalues的情况或简单的return语句中的对象)那么你可以从它移动,因为移动通常从移动的对象窃取 In any case, as I've said above, a new object is created. 无论如何,正如我上面所说,创建了一个新对象。

Quoting from http://eel.is/c++draft/class.copy.elision : 引自http://eel.is/c++draft/class.copy.elision

In the following copy-initialization contexts , a move operation might be used instead of a copy operation: 在以下复制初始化上下文中 ,可能会使用移动操作而不是复制操作:

(3.1) If the expression in a return statement ([stmt.return]) is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, or (3.1) 如果return语句 ([stmt.return]) 中的 表达式 是一个 (可能带括号的) id-expression,它指定一个对象,该对象具有在最内层封闭函数的body或parameter-declaration-clause中声明的自动存储持续时间或lambda表达式,或

(3.2) if the operand of a throw-expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), (3.2)如果throw-expression的操作数是非易失性自动对象的名称(除了函数或catch子句参数),其范围不会超出最内层封闭的try-block的末尾(如果有的话)是一个),

overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue . 首先执行重载决策以选择副本的构造函数,就好像该对象是由rvalue指定的一样

Consequently, if you return an automatic (local) variable: 因此,如果您返回自动(本地)变量:

vector<int> ident() {
  vector<int> v;
  return v;
};

then, v will be in return statement treated as an rvalue and therefore, the returned value will initialized be move constructor. 那么, v将在return语句中被视为rvalue ,因此,返回的值将被初始化为move构造函数。

However, these criteria are not met in your code, since v in your ident is not an automatic variable. 但是,您的代码中不满足这些条件,因为您的ident v不是自动变量。 Therefore, it is treated as an lvalue in the return statement and the return value is initiliazed by copy constructor from the vector referenced by the function parameter. 因此,它在return语句中被视为左值 ,并且返回值由函数参数引用的向量中的复制构造函数初始化。

These rules are quite natural. 这些规则很自然。 Imagine that compilers were allowed to move from all lvalues in return statements. 想象一下,编译器被允许从return语句中的所有左值移动。 Fortunately, they can only if they know that that lvalue is going to be destroyed, which holds for automatic variables and parameters passed by values in the context of return statements. 幸运的是,只有当他们知道左值将被破坏时,他们才能被破坏,这适用于自动变量和由return语句的上下文中的值传递的参数。

When a function returns by value, it will always create a new object. 当函数按值返回时,它将始终创建一个新对象。 The two vectors in your code are different for much the same reason that the following code produces two vectors: 代码中的两个向量是不同的,原因与以下代码生成两个向量的原因相同:

std::vector<int> v1; // create a vector
std::vector<int>& vr = v1; // create a reference to that vector, not a new vector
std::vector<int> v2 = vr; // create and copy-initialize a new vector from a reference,
// calling the copy constructor

It should be true that the two vectors here are logically equivalent at the point where v2 is created, by which I mean that after the copy, their sizes and contents are equal. 应该确实,这里的两个向量在创建v2的点逻辑上是等价的,我的意思是在复制之后,它们的大小和内容是相等的。 They are however distinct vectors, and changes to one vector thereafter will not change the other. 然而,它们是不同的向量,此后对一个向量的改变不会改变另一个向量。 You can read this from the variable types too; 你也可以从变量类型中读出这个; v1 is a vector , not a reference or pointer to a vector , and thus it is a unique object. v1是一个vector ,而不是一个vector或指向vector指针,因此它是一个唯一的对象。 The same holds for v2 . 对于v2

Also note that the compiler does always move construct the return value. 还要注意编译器总是移动构造返回值。 Return Value Optimatization (RVO) is a rule that allows the returned object to be constructed in place where it will be received by the caller, removing the need for a move or copy altogether 返回值Optimatization(RVO)是一个规则,允许将返回的对象构建调用者将接收到的位置,从而无需完全移动或复制

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

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