保证复制elision是否与函数参数一起使用?

时间:2017-05-29 16:07:56

标签: c++ copy-constructor c++17 object-lifetime copy-elision

如果我理解正确,从C ++ 17开始,此代码现在要求不进行任何复制:

Foo myfunc(void) {
    return Foo();
}

auto foo = myfunc(); // no copy

函数参数也是如此吗?是否会在以下代码中优化副本?

Foo myfunc(Foo foo) {
    return foo;
}

auto foo = myfunc(Foo()); // will there be copies?

2 个答案:

答案 0 :(得分:5)

在C ++ 17中,prvalues(" anonymous temporaries")不再是对象。相反,它们是关于如何构造对象的说明。

他们可以从他们的构造指令中实例化一个临时表,但是因为没有对象那里,所以没有复制/移动构造来消除。

Foo myfunc(Foo foo) {
  return foo;
}

所以在这里,函数参数foo被移动到prvalue返回值myfunc。您可以在概念上将其视为" myfunc返回有关如何制作Foo"的说明。如果这些说明是"未使用"通过您的程序,临时自动实例化并使用这些指令。

auto foo = myfunc(Foo());

所以在这里,Foo()是一个prvalue。它说"使用Foo构造函数"构造()。然后用它来构造myfunc的参数。不会出现任何省略,也不会调用复制构造函数或移动构造函数,只需()

东西发生在myfunc内。

myfunc返回Foo类型的prvalue。这个prvalue(又名构造指令)用于构造局部变量auto foo

所以这里发生的是Foo是通过()构建的,然后移入auto foo

据我所知,在C ++ 14和C ++ 17中不支持将函数参数省略为返回值(我可能错了,我这里没有标准的章节和章节)。但是,在return func_arg;上下文中使用它们时会隐式移动它们。

答案 1 :(得分:1)

是和否。引用cppreference

  

在下列情况下,编制者需要省略类对象的复制和移动构造[...]:

     
      
  • 在初始化中,如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类相同,则初始化表达式用于初始化目标对象

  •   
  • 在函数调用中,如果return语句的操作数是prvalue,并且函数的返回类型与该prvalue的类型相同。

  •   

因此,在您的第二个片段中,只会调用一个默认构造函数。首先,foo中的myFunc是从Foo()(1默认构造)初始化的,这是一个prvalue。这意味着它将被省略(见第1点)。

接下来,myFunc会返回foo的副本,由于foo不是prvalue(第2点),因此无法将其删除。因此,进行了一次移动,因为foo是xvalue。但是实际的返回值是一个prvalue,因为它是foo的新实例(在myFunc中),并且因为第一点它被省略了。

总之,标准保证一个默认构造和一个移动。不能再有了。但是,编译器实际上可能完全忽略了唯一的移动。