C++默认返回采用移动还是拷贝语义?C++17返回是否优先移动?
Great question, especially coming from C# where value type semantics work a bit differently! Let’s unpack this clearly, covering both built-in types like int and your user-defined type scenario.
First, a key point: C++ doesn’t default strictly to either move or copy semantics for returns—instead, it prioritizes copy elision (a compiler optimization that skips creating temporary objects entirely) above all else. When copy elision isn’t possible, C++11 and later will prefer move semantics if a valid move constructor is available; otherwise, it falls back to copy semantics.
Copy elision comes in two common flavors:
- Return Value Optimization (RVO): Applies to unnamed temporaries (e.g.,
return MyType();) - Named Return Value Optimization (NRVO): Applies to named local variables (e.g.,
MyType obj; return obj;)
Before C17, copy elision was an optional optimization. C17 made copy elision mandatory in specific cases (like returning a local object or a temporary), meaning the compiler has to skip the copy/move—no exceptions.
int ReturnSomething() in C++17 Let’s break down your code snippet:
int ReturnSomething(){ int i = 1; return i; }
For built-in types like int, there’s no practical difference between move and copy semantics—both just copy the raw bytes of the value directly. But even more importantly, C++17’s mandatory copy elision applies here. The compiler will initialize the return value directly in the caller’s memory space, skipping any "move" or "copy" of i entirely. There’s no temporary object created; i’s value is constructed right where it needs to be in the caller’s context.
Now, what about a custom type where you’ve explicitly defined a move constructor? Let’s use this example:
class MyType { public: MyType() = default; MyType(const MyType&) { /* Copy constructor logic */ } MyType(MyType&&) noexcept { /* Explicit move constructor logic */ } }; MyType ReturnCustomType() { MyType obj; return obj; }
- If NRVO is possible: The compiler will still prioritize copy elision, constructing
objdirectly in the caller’s return value slot—no move or copy occurs, even though a move constructor exists. This is always the most efficient path. - If NRVO isn’t possible: For example, if you return different named objects based on a branch:
Here, NRVO can’t be applied (since the return value could be eitherMyType ReturnCustomType(bool flag) { MyType obj1; MyType obj2; return flag ? obj1 : obj2; }obj1orobj2). In this case, C++ will prefer the move constructor over the copy constructor, since a valid move constructor is available and accessible. The returned object will be moved from whichever local variable is selected.
One caveat: If you explicitly disable copy elision (e.g., with compiler flags like -fno-elide-constructors), then the move will be used instead of copy when possible.
内容的提问来源于stack exchange,提问作者Reousa Asteron

