简体   繁体   English

为什么在返回右值时不调用移动构造函数?

[英]Why is the move constructor not invoked when returning an rvalue?

I have created a class Animal :我创建了一个 class Animal

class Animal {
public:
    Animal() = default;
    Animal(Animal&& a) = delete;
    Animal(Animal& a) = delete;

    Animal& operator=(Animal&& a) = delete;
    Animal& operator=(Animal& a) = delete;
};

Animal func1() {
    return Animal();
}

Animal func2() {
    Animal a {};
    return a;
}

int main(void) {
    Animal a1 = func1();
    Animal a2 = func2();
    return 0;
}

I don't understand why func1() works fine: return Animal() creates an rvalue object and I initialize a1 with this object in the main function;我不明白为什么func1()工作正常: return Animal()创建一个右值 object 并且我在main function 中用这个 object 初始化a1 as I see it, it is equal to在我看来,它等于

Animal a1 = func1()
         ==
Animal a1 = Animal&& temp   //(I wrote type in assignment for clarifying)

and I have read that the return value is an rvalue;我已经读到返回值是一个右值; however, in func2 I get an error that 'copy constructor is deleted' not the move constructor, why?然而,在func2中,我得到一个错误,“复制构造函数被删除”而不是移动构造函数,为什么?

Case 1情况1

Here we consider the statement:这里我们考虑语句:

 Animal a1 = func1();

The call expression func1() is an rvlalue of type Animal .调用表达式func1()Animal类型的Animal值。 And from C++17 onwards, due to mandatory copy elison :而从 C++17 开始,由于强制复制 elison

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects.在以下情况下,编译器需要省略 class 对象的复制和移动构造,即使复制/移动构造函数和析构函数具有可观察到的副作用。 The objects are constructed directly into the storage where they would otherwise be copied/moved to.对象被直接构建到存储中,否则它们将被复制/移动到那里。 The copy/move constructors need not be present or accessible :复制/移动构造函数不需要存在或可访问

  • In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type:在 return 语句中,当操作数是与 function 返回类型相同的 class 类型(忽略 cv 限定)的纯右值时:

That is, the object is constructed directly into the storage where they would otherwise be copied/moved to.也就是说,object 被直接构建到存储中,否则它们将被复制/移动到那里。 That is, in this case(for C++17), there is no need of a copy/move constructor to be available.也就是说,在这种情况下(对于 C++17),不需要复制/移动构造函数可用。 And so this statement works.所以这个声明是有效的。

Case 2案例二

Here we consider the statement:这里我们考虑语句:

Animal a2 = func2();

Here from non mandatory copy elison ,这里来自非强制复制 elison

Under the following circumstances, the compilers are permitted, but not required to omit the copy and move (since C++11) construction of class objects even if the copy/move (since C++11) constructor and the destructor have observable side-effects.在以下情况下,允许但不要求编译器省略 class 对象的复制和移动 (C++11 起)构造,即使复制/移动 (C++11 起)构造函数和析构函数具有可观察端-效果。 The objects are constructed directly into the storage where they would otherwise be copied/moved to.对象被直接构建到存储中,否则它们将被复制/移动到那里。 This is an optimization: even when it takes place and the copy/move (since C++11) constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed:这是一个优化:即使它发生并且复制/移动(C++11 起)构造函数没有被调用,它仍然必须存在并且可以访问(就好像根本没有优化发生一样),否则程序是错误的-形成:

  • In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type.在 return 语句中,当操作数是具有自动存储持续时间的非易失性 object 的名称时,它不是 function 参数或 catch 子句参数,并且与 class 类型相同(忽略 cv 限定) function 返回类型。

That is, the copy/move constructors are required to exist(that is these ctors must be present and accessible) but since you've explicitly marked them as deleted this statement fails with the error :也就是说,复制/移动构造函数必须存在(即这些构造函数必须存在且可访问)但是由于您已明确将它们标记为已删除此语句失败并出现错误

error: use of deleted function ‘Animal::Animal(Animal&&)’

The error can also be seen here错误也可以在这里看到

In C++17 there is a new set of rules about temporary materialization.在 C++17 中有一套关于临时物化的新规则。

In a simple explanation an expression that evaluates to a prvalue (a temporary) doesn't immediately create an object, but is instead a recipe for creating an object. So in your example Animal() doesn't create an object straight away so that's why you can return it even if the copy and move constructors are deleted.在一个简单的解释中,计算为纯右值(临时)的表达式不会立即创建 object,而是创建 object 的方法。因此在您的示例中, Animal()不会立即创建 object,所以这是为什么即使删除了复制和移动构造函数也可以返回它。

In your main assigning the prvalue to a triggers the temporary materialization so the object is only now created directly in the scope of main .在您的 main 中,将 prvalue 分配给a会触发临时物化,因此 object 现在只是直接在main的 scope 中创建。 Throught all of this there is a single object so there is no operation of copy or move.在所有这些过程中,只有一个 object,因此没有复制或移动操作。

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

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