为什么在返回右值时不调用移动构造函数?
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()
是类型 Animal
的 rvlalue。从 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
的范围内直接创建对象。所有这一切只有一个对象,因此没有复制或移动操作。
我创建了一个 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()
是类型 Animal
的 rvlalue。从 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
的范围内直接创建对象。所有这一切只有一个对象,因此没有复制或移动操作。