从函数(按值)和隐式移动规则返回类的命名对象?



我有一个问题,理解当你返回一个类的对象(不是一个特定的类)形成一个函数(按值传递)时会发生什么在此代码中:示例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;
}

最新更新