Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ return语句是否为按值返回的函数创建临时对象?_C++_C++11 - Fatal编程技术网

C++ return语句是否为按值返回的函数创建临时对象?

C++ return语句是否为按值返回的函数创建临时对象?,c++,c++11,C++,C++11,在学习C++11右值引用和移动语义时,我开始对函数如何返回值来初始化变量感到困惑。请看以下示例: Widget makeWidget() { Widget w; … return w; } Widget w1 = makeWidget(); Widget makeWidget() { Widget w; … return std::move(w); } Widget w1 = makeWidget(); 这里我假设没有RVO(即编译不会省略复制

在学习C++11右值引用和移动语义时,我开始对函数如何返回值来初始化变量感到困惑。请看以下示例:

Widget makeWidget()
{
    Widget w;
    …
    return w;
}
Widget w1 = makeWidget();
Widget makeWidget()
{
    Widget w;
    …
    return std::move(w);
}
Widget w1 = makeWidget();
这里我假设没有RVO(即编译不会省略复制/移动)。执行return语句时
returnw,功能是否:

1)复制初始化一个临时对象,该对象的值成为函数的返回值,位于一个已知位置(调用方知道的某个固定寄存器或内存位置)?然后调用方获取该对象以复制初始化
w1
?或

2)函数获取调用方传递的
w1
内存位置,函数的自动变量
w
用于复制初始化
w1
?(这是否已经是某种RVO?或者某种内联函数行为?或者它是一种可能的调用约定?)

如果是第一种情况,并且return创建一个临时的,那么将有两个复制构造函数调用,一个创建临时的,一个创建
w1
。如果是第二种情况,则只有一个复制构造函数调用来创建
w1

现在假设我们有RVO。然后,如果返回的行为是case1),那么编译器可以通过直接在已知的返回值位置构造
w
来省略临时的复制构造。如果return的行为类似于case2),那么在这种情况下,RVO甚至可以直接在为
w1
分配的内存位置中创建自动变量
w
。我对RVO的理解正确吗


现在让我添加移动语义。再次假设没有RVO,并查看以下示例:

Widget makeWidget()
{
    Widget w;
    …
    return w;
}
Widget w1 = makeWidget();
Widget makeWidget()
{
    Widget w;
    …
    return std::move(w);
}
Widget w1 = makeWidget();
现在再次针对上述两种情况:

1)我是否希望有两个移动:第一个移动来自使用表达式
std::move(w)
初始化temp
Widget
对象;第二步是使用temp
Widget
初始化
w1
,这是一个右值?或

2)只有一个移动:使用表达式
std::move(w)
初始化
w1


最后一个问题:返回行为是否取决于它返回的是POD类型还是类类型

1) 复制初始化临时对象,其值将成为 函数的返回值,位于已知位置(某些固定寄存器 或者调用方知道的内存位置)?然后调用者获取这个对象 复制初始化w1

大致如此。通常,调用方分配必要的堆栈空间来保存返回值,并将指向该值的指针作为隐藏参数传递

请注意,这两个副本初始化实际上都是移动

然后,编译器可以通过 直接在已知位置构造
w
以获取返回值

对(但这是一个动作)。它还可以通过将其地址作为应该构造返回值的位置来传递,从而进一步省略
w1
的构造

这是一个不可删除的移动(从
std::move(w)
到临时返回值)和一个可删除的移动(从临时返回值到
w1

返回行为是否取决于它返回的是POD类型还是POD类型 班级类型


它可以。根据平台ABI的不同,一些小型POD类型可能会在寄存器中返回。

大多数编译器的调用约定是,调用者传入指向原始空间的指针,函数在其中构造返回值。至少在效率最低的情况下(根本没有RVO),临时副本是从该空间中的
w
构建的,然后
w1
是从该临时副本构建的副本。大多数编译器都会省略其中一个或两个副本,后者通过将分配给
w1
的空间传递给函数,从而将返回值直接构造到
w1
中;前者通过使
w
成为同一空间的别名,实际上使
w
w1
引用同一对象。