C++ C++;编译器决定何时调用std::vector或任何对象的移动构造函数

C++ C++;编译器决定何时调用std::vector或任何对象的移动构造函数,c++,c++11,stl,C++,C++11,Stl,我在读std模板库的书,对STL容器一章中列出的以下细节感到困惑。 显然,它指定了STD::VECTOR操作和效果 Operation Effect vector<Elem> c(c2) | Copy constructor; creates a new vector as a copy of c2 (all elements are copied) vector<Elem> c = c2 | Copy constructor;

我在读std模板库的书,对STL容器一章中列出的以下细节感到困惑。 显然,它指定了STD::VECTOR操作和效果

Operation                     Effect

vector<Elem> c(c2)  | Copy constructor; creates a new vector as a copy of c2 (all elements are copied)
vector<Elem> c = c2 | Copy constructor; creates a new vector as a copy of c2 (all elements are copied)
vector<Elem> c(rv)  | Move constructor; creates a new vector, taking the contents of the rvalue rv (since C++11)
vector<Elem> c = rv | Move constructor; creates a new vector, taking the contents of the rvalue rv (since C++11)
操作效果
向量c(c2)|复制构造函数;创建一个新向量作为c2的副本(复制所有元素)
向量c=c2 |复制构造函数;创建一个新向量作为c2的副本(复制所有元素)
向量c(rv)|移动构造函数;创建一个新的向量,获取右值rv的内容(从C++11开始)
向量c=rv |移动构造函数;创建一个新的向量,获取右值rv的内容(从C++11开始)

显然,move构造函数和copy构造函数的语法没有区别,它们的确切调用时间是什么时候?

copy构造函数的语法: 类名(常量类名&)

移动构造函数的语法: 类名(类名&&)

调用在一起看起来相同,但它们的声明不同

参考:


假设您有一个函数
f
,它按值返回向量:

std::vector<int> f();
std::vector f();
函数返回的值是右值

然后假设你想调用这个函数来初始化你的向量:

std::vector<int> v = f();
std::vector v=f();
现在编译器知道由
f
返回的向量将不再使用,它是一个临时对象,因此复制这个临时对象没有意义,因为它将立即被销毁。因此编译器决定调用move构造函数

显然,move和copy构造函数的语法没有区别

事实上,这是事实。这也不特定于
std::vector
,但通常适用于所有类型。通过复制和移动进行复制初始化的语法完全相同。拷贝分配也是如此

区别来自参数表达式的类型。当参数为非常量r值时,重载解析首选移动构造函数/赋值。否则,移动构造函数不适用,因此使用复制构造函数

我假设rv只是一个像c2一样的构造对象


rv和c2似乎不是对象。它们显然是表达式的占位符。这本书可以更清楚地说明这一点(也许是这样,摘录毕竟是断章取义的)。

也许你应该将语法和语义分离开来,以便更好地理解。让我来类推,考虑这个代码。

struct A {
  A(int i) {}
  A(const std::string& s) {}
};
现在,如果您找到以下行,将调用哪个构造函数

A a(x);

您不能告诉它,因为您不知道
x
的类型是int还是std::string。在您的示例中没有太大不同,但使用移动语义,编译器将检查参数是否为右值。如果这样一个
A
类提供了一个(移动)构造函数重载,并且
x
是一个右值引用,那么它将是首选的。

在调用移动构造函数有意义的地方调用移动构造函数,即移动一个对象并在其后使用它没有意义,因为原始对象已被修改(可能),这是不可取的:

std::vector<int> a = { 1 };
std::vector<int> b = a; //Let's say this called move constructor
int value = a[0]; //value is possibly not 1, the value may have changed due to the move
std::vector a={1};
std::向量b=a//假设这个叫做移动构造函数
整数值=a[0]//值可能不是1,该值可能因移动而更改
因此,在这种情况下,复制构造函数被称为:

std::vector<int> a = { 1, 2 };
std::vector<int> b = a; //Copy constructor
std::vector a={1,2};
std::向量b=a//复制构造函数
但是,在这里它确实调用了move构造函数,因为它被分配给一个右值或一个临时值:

void foo(std::vector<int>) {}
foo({ 1, 2 }); //move constructor
voidfoo(std::vector){
foo({1,2})//移动构造函数

向量
{1,2}
是一个临时向量,谁在乎它是否被修改了?你永远不会知道,因为一旦
foo
结束,向量就会被破坏。复制临时向量只会浪费时间。

如果没有显式的
std::move
调用,这取决于编译器。规范中可能有一些规则,但恐怕主要取决于编译器。如果您传递一个r值,那么编译器就足够聪明,可以调用移动构造函数。另外请注意,在初始化变量时,使用
=
并不意味着以与以后执行赋值相同的方式进行赋值。例如:
inta=5
是一个初始化,而
int是一个初始化;a=5
是一个赋值。@JoachimPileborg我的困惑是编译器什么时候决定调用move或copy构造函数,这两个调用对两个都是一样的。看看
c2
rv
之间的区别:
rv
是一个r值,如果您试图复制或分配它,编译器将调用构造函数/运算符的移动版本(如果存在)。@Revolver\u Ocelot:我假设rv只是一个像c2一样的构造对象,请解释一下如果有什么不同,如果用法相同,编译器调用移动构造函数的原因是什么。编译器如何决定任何想法?要调用移动构造函数,您可以这样做:T a(std::move(b))。这是显式的。对于隐式的情况,请阅读ref了解更多详细信息:@LearningCpp-查看JoachimPileborg的第一条注释:如果您编写
std::vector v1;std::vector v2(v1);
,for
v2
被称为复制构造函数,因为您可以再次使用
v1
;如果您编写
std::vector v1;std::vector v2(std::move(v1));
,for
v2
被称为移动构造函数,因为在
std::move()
中,您对编译器说“我不再使用
v1
”.哦,这都取决于r值的存在吗?如果我wrong@LearningCpp是的,
&&
表示右值引用。但是,这可能不会被移动,而是RVO进入:(cast((从C_DATASETKEYCM_REP中选择M_TODAY_REF)作为数字(10))作为M_REF_数据2,