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

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

我创建了一个 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;
}

我不明白为什么 func1() 工作正常:return Animal() 创建一个右值对象,我在 main 函数中用这个对象初始化 a1;如我所见,它等于

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

而且我读到 return 值是一个右值;然而,在 func2 中,我得到一个错误,“复制构造函数被删除”而不是移动构造函数,为什么?

案例一

这里我们考虑语句:

 Animal a1 = func1();

调用表达式 func1() 是类型 Animalrvlalue。从 C++17 开始,由于 mandatory copy 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. 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:

也就是说,对象被直接构建到存储中,否则它们将 copied/moved 到。也就是说,在这种情况下(对于 C++17),不需要 copy/move 构造函数可用。所以这个声明有效。

案例二

这里我们考虑语句:

Animal a2 = func2();

这里来自non mandatory copy 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. 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:

  • 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.

也就是说,copy/move 构造函数必须存在(即这些构造函数必须存在且可访问)但是由于您已明确将它们标记为 已删除此语句因 error:

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

错误也能看到here

在 C++17 中有一组关于临时物化的新规则。

在一个简单的解释中,计算结果为纯右值(临时)的表达式不会立即创建对象,而是创建对象的方法。因此,在您的示例中,Animal() 不会立即创建对象,因此即使删除了复制和移动构造函数,您也可以 return 它。

在您的 main 中,将纯右值分配给 a 会触发临时物化,因此现在仅在 main 的范围内直接创建对象。所有这一切只有一个对象,因此没有复制或移动操作。