c++编译器如何决定何时为std::vector或任何对象调用move构造函数?

How does a C++ compiler decide when to call a move constructor for std::vector or any object

本文关键字:任何 vector 对象 调用 构造函数 move std 何决定 编译器 何时 决定      更新时间:2023-10-16

我正在阅读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)

显然,移动构造函数和复制构造函数的语法没有区别,它们到底在什么时候被调用?

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

std::vector<int> f();

函数返回值为右值

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

std::vector<int> v = f();

现在编译器知道f返回的向量将不再被使用,它是一个临时对象,因此复制这个临时对象是没有意义的,因为它将立即被销毁。所以编译器决定调用move构造函数。

move构造函数是在有意义的地方调用的,也就是说,移动一个对象之后再使用它是没有意义的,因为原始对象已经被修改了(可能),这是不可取的:

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<int> a = { 1, 2 };
std::vector<int> b = a; //Copy constructor

但是,这里调用move构造函数,因为它被赋值给右值,或者临时值:

void foo(std::vector<int>) {}
foo({ 1, 2 }); //move constructor

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

显然,移动构造函数和复制构造函数的语法没有区别

确实如此。这也不是std::vector所特有的,但对于所有类型都是通用的。通过复制初始化和通过移动初始化具有完全相同的语法。复制分配也是如此。

区别在于实参表达式的类型。当实参是非constr值时,重载解析会优先使用move构造函数/赋值。否则,move-constructor不适用,所以使用copy constructor。

我假设rv只是一个像c2

一样的构造对象

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

也许您应该将语法与语义分离以便更好地理解。让我做一个类比,考虑以下代码:

struct A {
  A(int i) {}
  A(const std::string& s) {}
};

现在,如果您找到以下行,将调用哪个构造函数?

A a(x);

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

复制构造函数的语法:Classname (const Classname &) move构造函数的

语法:classname (classname&,)

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

裁判:

http://en.cppreference.com/w/cpp/language/copy_constructorhttp://en.cppreference.com/w/cpp/language/move_constructor

相关文章: