在 C++ 中,对 return 使用移动操作是什么意思?
In C++, what does it mean to use a move operation on return?
我正在通读 Bjarne Stroustrup 的 C++ 编程语言(第 4 版)和第 1 页。 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)
也在第 1 页上。 517 他说:
[The object] has a move constructor so that "return by value" is
simple and effecient as well as "natural"
如果 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());
}
a
和 b
不应该指向相同的对象吗?
vector<int> foo() {
return ...;
};
函数 returns 按值计算。所以 returned 对象是一个新创建的对象。无论对象是如何创建的,都是如此:通过复制构造、移动构造、默认构造或任何类型的构造。
建设copy/move主要是效率问题。如果您创建的对象在之后被使用,那么您只能复制它。但是如果你知道你创建的对象在之后不再使用(就像 prvalues 或简单 return 语句中的对象的情况)那么你可以从它移动,因为移动通常 窃取 从对象中移出。无论如何,正如我上面所说的,一个新的对象被创建了。
当一个函数returns被赋值时,它总是会创建一个新的对象。代码中的两个向量不同,原因与以下代码生成两个向量的原因大致相同:
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
这里的两个向量在v2
被创建的地方逻辑上应该是等价的,我的意思是在复制之后,它们的大小和内容是平等的。然而,它们是不同的向量,此后对一个向量的更改不会更改另一个向量。您也可以从变量类型中读取它; v1
是一个 vector
,不是对 vector
的引用或指针,因此它是一个唯一的对象。 v2
.
也是如此
另请注意,编译器确实总是移动构造 return 值。 Return 值优化 (RVO) 是一种规则,它允许 returned 对象在 就地 被调用者接收的位置构建,从而消除了需要完全移动或复制
引用自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.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),
overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
因此,如果您 return 一个自动(局部)变量:
vector<int> ident() {
vector<int> v;
return v;
};
然后,v
将在 return
语句中被视为 rvalue,因此,returned 值将被初始化为 move构造函数。
但是,您的代码不满足这些条件,因为您的 ident
中的 v
不是自动变量。因此,它在 return
语句中被视为 lvalue 并且 return 值由函数参数引用的向量中的复制构造函数初始化。
这些规则很自然。想象一下,允许编译器从 return
语句中的所有左值移动。幸运的是,只有当他们知道左值将被销毁时,他们才能这样做,这适用于在 return
语句的上下文中通过值传递的自动变量和参数。
我正在通读 Bjarne Stroustrup 的 C++ 编程语言(第 4 版)和第 1 页。 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)
也在第 1 页上。 517 他说:
[The object] has a move constructor so that "return by value" is simple and effecient as well as "natural"
如果 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());
}
a
和 b
不应该指向相同的对象吗?
vector<int> foo() {
return ...;
};
函数 returns 按值计算。所以 returned 对象是一个新创建的对象。无论对象是如何创建的,都是如此:通过复制构造、移动构造、默认构造或任何类型的构造。
建设copy/move主要是效率问题。如果您创建的对象在之后被使用,那么您只能复制它。但是如果你知道你创建的对象在之后不再使用(就像 prvalues 或简单 return 语句中的对象的情况)那么你可以从它移动,因为移动通常 窃取 从对象中移出。无论如何,正如我上面所说的,一个新的对象被创建了。
当一个函数returns被赋值时,它总是会创建一个新的对象。代码中的两个向量不同,原因与以下代码生成两个向量的原因大致相同:
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
这里的两个向量在v2
被创建的地方逻辑上应该是等价的,我的意思是在复制之后,它们的大小和内容是平等的。然而,它们是不同的向量,此后对一个向量的更改不会更改另一个向量。您也可以从变量类型中读取它; v1
是一个 vector
,不是对 vector
的引用或指针,因此它是一个唯一的对象。 v2
.
另请注意,编译器确实总是移动构造 return 值。 Return 值优化 (RVO) 是一种规则,它允许 returned 对象在 就地 被调用者接收的位置构建,从而消除了需要完全移动或复制
引用自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.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),
overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
因此,如果您 return 一个自动(局部)变量:
vector<int> ident() {
vector<int> v;
return v;
};
然后,v
将在 return
语句中被视为 rvalue,因此,returned 值将被初始化为 move构造函数。
但是,您的代码不满足这些条件,因为您的 ident
中的 v
不是自动变量。因此,它在 return
语句中被视为 lvalue 并且 return 值由函数参数引用的向量中的复制构造函数初始化。
这些规则很自然。想象一下,允许编译器从 return
语句中的所有左值移动。幸运的是,只有当他们知道左值将被销毁时,他们才能这样做,这适用于在 return
语句的上下文中通过值传递的自动变量和参数。