我有一个问题,理解当你返回一个类的对象(不是一个特定的类)形成一个函数(按值传递)时会发生什么在此代码中:示例1
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class test {
public:
test(int y) {
printf(" test(int y)n");
}
test() {
printf(" test()n");
}
test( const test& z) {
printf(" test( const test&z)n");
}
test(test&& s)noexcept{
printf(" test(test&& s)n");
}
test& operator=(test e) {
printf(" test& operator=( test e)n");
return *this;
}
};
test Some_thing() {
test i;
return i;
}
int main()
{
Some_thing();
return 0;
}
输出:
test()
test(test&& s)
前面的输出让我明白,在函数(Some_thing())作用域中创建了两个对象。第一个是左值对象,我们在函数(Some_thing())的第一行创建它,并给它一个名字(i),所以构造函数test ( )
被调用。第二个是右值对象,因此调用构造函数test ( test&& s )
。
但是当我删除这个构造函数test(test&& s)noexcept
并改变这个构造函数
test( const test& z)
到
test( test& z)
并再次运行代码:
示例2#include<iostream>
#include<vector>
#include<string>
using namespace std;
class test {
public:
test(int y) {
printf(" test(int y)n");
}
test() {
printf(" test()n");
}
test( test& z) {
printf(" test( test&z)n");
}
test& operator=(test e) {
printf(" test& operator=( test e)n");
return *this;
}
};
test Some_thing() {
test i;
return i;
}
int main()
{
Some_thing();
return 0;
}
输出:
test()
test( test&z)
虽然我预计这段代码将无法编译,因为没有构造函数接受test&&
或const test&
作为参数
和当我试图添加一行到前面的代码是test(test&& z) = delete
示例3
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class test {
public:
test(test&& z) = delete;
test(int y) {
printf(" test(int y)n");
}
test() {
printf(" test()n");
}
test( const test& z) {
printf(" test( test&z)n");
}
test& operator=(test e) {
printf(" test& operator=( test e)n");
return *this;
}
};
test Some_thing() {
test i;
return i;
}
int main()
{
Some_thing();
return 0;
}
我试图编译它,但它无法编译,也无法运行
示例2是如何编译和运行??????的以及如何使用构造函数test( test&z)
代替test(test&& z)
? ?
(我的意思是test( test&z)
不是test( const test&z)
,所以test( test&z)
不能代替test(test&& z)
)
编辑:这段代码编译并运行:示例4
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class test {
public:
test(test&& z) = delete;
test(int y) {
printf(" test(int y)n");
}
test() {
printf(" test()n");
}
test(const test& z) {
printf(" test( test&z)n");
}
test& operator=(test e) {
printf(" test& operator=( test e)n");
return *this;
}
};
int main()
{
test u;
test r(u);
return 0;
}
输出:
test()
test( test&z)
程序的行为可以通过自动从局部变量和参数移动来理解:
如果表达式是一个id表达式(可能带括号),命名一个类型为
的变量
非易失性对象类型或
对对象类型的非易失性右值引用(c++ 20起)
并且变量被声明为
正文或者
作为
的参数最里面的封闭函数或lambda表达式,
则重载解析选择用于初始化返回值的构造函数,或者对于co_return,选择promise.return_value()的重载(c++ 20起)执行两次:
- first as if expression是右值表达式(因此它可以选择move构造函数),
- 如果第一个过载解析失败或
- 它成功了,但没有选择move构造函数(正式地,所选构造函数的第一个参数不是对(可能是cv限定的)表达式类型的右值引用)(直到c++ 20)
- 则像往常一样执行重载解析,将表达式视为左值(所以它可以选择复制构造函数)。
现在,让我们逐个应用到你的代码片段中。
示例1在本例中,由于移动元素可用且可行,条件&;首先,如果表达式是右值表达式&;满足,因此选择移动元素,我们得到上述输出。
class test {
public:
test(int y) {
printf(" test(int y)n");
}
test() {
printf(" test()n");
}
test( const test& z) {
printf(" test( const test&z)n");
}
test(test&& s)noexcept{
printf(" test(test&& s)n");
}
test& operator=(test e) {
printf(" test& operator=( test e)n");
return *this;
}
};
test Some_thing() {
test i;
return i;
}
int main()
{
Some_thing();
return 0;
}
示例2 在本例中,由于您提供了复制函数test::test( test&)
,编译器不会为我们合成移动函数。请注意,没有合成移动因子与删除移动因子是不同的。因此,条件"如果第一次重载解析失败">满足(因为没有移动因子),然后执行第二次重载解析,现在将选择提供的复制因子,从而得到上述输出。
class test {
public:
test(int y) {
printf(" test(int y)n");
}
test() {
printf(" test()n");
}
test( test& z) {
printf(" test( test&z)n");
}
test& operator=(test e) {
printf(" test& operator=( test e)n");
return *this;
}
};
test Some_thing() {
test i;
return i;
}
int main()
{
Some_thing();
return 0;
}
示例3
在本例中,您已经显式地删除了移动因子。也就是说,你的意图是,如果有人试图使用move元素,那么它应该失败。所以在这里,当重载解析第一次发生时,move元素被选中,但是由于你已经显式地将它标记为已删除,因此立即失败,因此出现错误。
class test {
public:
test(test&& z) = delete;
test(int y) {
printf(" test(int y)n");
}
test() {
printf(" test()n");
}
test( const test& z) {
printf(" test( test&z)n");
}
test& operator=(test e) {
printf(" test& operator=( test e)n");
return *this;
}
};
test Some_thing() {
test i;
return i;
}
int main()
{
Some_thing();
return 0;
}