在C++;,返回时使用移动操作意味着什么? 我正在阅读Bjarne Stroustrup的C++程序设计语言(第四版)和P。516他说:

在C++;,返回时使用移动操作意味着什么? 我正在阅读Bjarne Stroustrup的C++程序设计语言(第四版)和P。516他说:,c++,return,move-semantics,C++,Return,Move Semantics,编译器如何知道何时可以使用移动操作而不是 而不是复制操作?在少数情况下,例如对于返回值 语言规则说它可以(因为下一个动作被定义为 销毁元素) 也在第页。517他说: [对象]有一个移动构造函数,因此可以使用“按值返回” 简单、高效且“自然” 如果return总是使用移动操作,那么为什么像下面这样的操作不起作用 #include <vector> #include <assert.h> using namespace std; vector<int> ide

编译器如何知道何时可以使用移动操作而不是 而不是复制操作?在少数情况下,例如对于返回值 语言规则说它可以(因为下一个动作被定义为 销毁元素)

也在第页。517他说:

[对象]有一个移动构造函数,因此可以使用“按值返回” 简单、高效且“自然”

如果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());
}
#包括
#包括
使用名称空间std;
矢量标识(矢量和v){
返回v;
};
int main(){
向量a{};
常数向量&b=标识(a);
a、 推回(1);
断言(a.size()==b.size());
}
a
b
不应该指向相同的对象吗?

vector foo(){
vector<int> foo() {
    return ...;
};
返回。。。; };
该函数按值返回。因此,返回的对象是新创建的对象。无论对象是如何创建的:通过复制构造、移动构造、默认构造或任何类型的构造,这都是正确的


复制/移动构造的问题主要是效率问题。如果从中创建的对象在之后使用,则只能复制它。但是,如果您知道从中创建的对象在之后不再使用(例如prvalues或简单返回语句中的对象),那么您可以从中移动,因为move通常从移动的对象中窃取。在任何情况下,正如我上面所说,都会创建一个新对象。

当函数按值返回时,它将始终创建一个新对象。代码中的两个向量不同,原因与以下代码生成两个向量的原因大致相同:

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
std::vector v1;//创建一个向量
标准::向量&vr=v1;//创建对该向量的引用,而不是新向量
标准::向量v2=vr;//从引用创建并复制并初始化新向量,
//调用复制构造函数
的确,这里的两个向量在创建
v2
时在逻辑上是等价的,这意味着在复制之后,它们的大小和内容是相等的。然而,它们是不同的向量,此后对一个向量的更改不会更改另一个向量。您也可以从变量类型中读取此信息
v1
是一个
向量
,不是指向
向量
的引用或指针,因此它是一个唯一的对象。这同样适用于
v2

还请注意,编译器总是移动返回值。返回值优化(RVO)是一条规则,允许在调用者接收返回对象的位置构造返回对象,从而完全不需要移动或复制:

在以下复制初始化上下文中可以使用移动操作代替复制操作:

(3.1)如果返回语句([stmt.return])中的表达式是一个(可能加括号)id表达式,该表达式命名了一个对象,该对象的自动存储持续时间在最里面的封闭函数或lambda表达式的主体或参数声明子句中声明,或者

(3.2)如果一个抛出表达式的操作数是一个非易失性自动对象(函数或catch子句参数除外)的名称,该对象的范围不超出最里面的封闭try块(如果有)的末尾

首先执行重载解析以选择副本的构造函数,就像对象由右值指定一样

因此,如果返回自动(局部)变量:

vector<int> ident() {
  vector<int> v;
  return v;
};
vector ident(){
向量v;
返回v;
};
然后,
v
将在
return
语句中作为右值处理,因此,返回值将初始化为move构造函数

但是,代码中不符合这些标准,因为
ident
中的
v
不是自动变量。因此,它在
return
语句中被视为左值,返回值由函数参数引用的向量的copy构造函数初始化


这些规则很自然。假设编译器被允许从
return
语句中的所有左值移动。幸运的是,只有当他们知道左值将被销毁时,他们才能这样做,因为在
return
语句的上下文中,通过值传递的自动变量和参数是有效的。

a和b不是指针,也不指向任何东西。我很确定,移动返回在引用上不是自动的。它必须是一个局部变量,因为关键是它可以移动,因为它正在被破坏。引用未被销毁。@ZanLynx局部变量或通过值传递的参数。后者也是一个非常重要的例子,只是澄清一下。@DanielLangr我必须仔细检查标准或其他文档,但我相信参数是局部变量,不是吗?它们早在K&R C中就存在了。事实上,我认为一些C实现在堆栈上传递参数的方式与局部变量在堆栈上的布局方式相同,而且由于没有函数原型,如果局部变量声明与传递的内容匹配,一切都正常…@ZanLynx你似乎是对的,值传递的参数也是局部变量。谢谢你指出这一点。