问题
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