Does guaranteed copy elision work with function parameters?

隐身守侯 提交于 2019-12-21 13:10:04

问题


If I understood correctly, starting from C++17, this code now requires that no copy will be done:

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

auto foo = myfunc(); // no copy

Is it also true for function arguments? Will copies be optimized away in the following code?

Foo myfunc(Foo foo) {
    return foo;
}

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

回答1:


In C++17, prvalues ("anonymous temporaries") are no longer objects. Instead, they are instructions on how to construct an object.

They can instantiate a temporary from their construction instructions, but as there is no object there, there is no copy/move construction to elide.

Foo myfunc(Foo foo) {
  return foo;
}

So here, the function argument foo is moved into the prvalue return value of myfunc. You can think of this conceptually as "myfunc returns instructions on how to make a Foo". If those instructions are "not used" by your program, a temporary is automatically instantiated and uses those instructions.

auto foo = myfunc(Foo());

So here, Foo() is a prvalue. It says "construct a Foo using the () constructor". It is then used to construct the argument of myfunc. No elision occurs, no copy constructor or move constructor is called, just ().

Stuff then happens inside myfunc.

myfunc returns a prvalue of type Foo. This prvalue (aka construction instructions) is used to construct the local variable auto foo.

So what happens here is that a Foo is constructed via (), then moved into auto foo.

Elision of function arguments into return values is not supported in C++14 nor C++17 as far as I know (I could be wrong, I do not have chapter and verse of the standard here). However, they are implicitly moved when used in a return func_arg; context.




回答2:


Yes and no. Quoting cppreference:

Under the following circumstances, the compilers are required to omit the copy- and move- construction of class objects [...]:

  • In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object

  • In a function call, if the operand of a return statement is a prvalue and the return type of the function is the same as the type of that prvalue.

So, in your second snippet only one default constructor will be called. First, foo in myFunc is initialized from Foo() (1 default construction), which is a prvalue. This means that it will be elided (see point 1).

Next, myFunc returns a copy of foo, which cannot be elided as foo is not a prvalue (point 2). So, one move is made, as foo is a xvalue. But the actual return value is a prvalue, as it is a new instance of foo (in myFunc), and because of point one it is elided.

In conclusion, one default construction and one move is guaranteed by the Standard. There cannot be any more. But, the compiler might actually elide the only move altogether.



来源:https://stackoverflow.com/questions/44246634/does-guaranteed-copy-elision-work-with-function-parameters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!