C++中Lambda表达式返回值赋值是否会产生拷贝?
static MyObject something = []() { ... return ret; }(); cause a copy? Great question—this is a common point of confusion when using lambda initializers for static objects, especially when transitioning between C14 and C17. Let’s break this down for your specific environment (VS2017/C14 soon moving to C17):
C++14 (VS2017) Behavior
In C++14, the compiler may perform Named Return Value Optimization (NRVO) here. NRVO allows the compiler to eliminate the copy/move by constructing the local ret object directly in the memory allocated for something.
- VS2017 has solid support for NRVO in this scenario, so in practice, you likely won’t see a copy or move happen.
- However, NRVO is not mandatory in C14. The standard requires that the copy/move constructor must still be accessible (i.e., not deleted or private) even if the compiler optimizes it away. If
MyObjecthas a deleted copy constructor, this code would fail to compile in C14, even if no copy is actually performed.
C++17 Behavior
C++17 fixes this ambiguity with mandatory copy elision. For cases like this—where you return a local object from a function (or lambda) to initialize a variable—the compiler must eliminate any copy or move operations.
The compiler will directly construct the ret object in the storage allocated for something, skipping any intermediate temporary objects entirely. Even if MyObject has deleted copy/move constructors, this code will compile successfully in C++17.
How to Avoid Copies in C++14 (If You Want Guaranteed Safety)
If you want to ensure no copies happen even if NRVO isn’t applied (though VS2017 will almost always apply it), you have a couple of options:
- Use
std::moveto transfer ownership: Modify your lambda to return a moved object:
This will trigger a move constructor instead of a copy (assumingstatic MyObject something = []() { MyObject ret(...); ret.x(...); return std::move(ret); }();MyObjecthas a move constructor, which it should if you’ve defined modern C++ types). Moves are typically much cheaper than copies, and this doesn’t rely on optimizer behavior. - Return a temporary object directly: If
MyObject’s methods return*this(like a fluent interface), you can construct and modify it in one line:
This uses RVO (Return Value Optimization), which is more widely supported than NRVO in older compilers, and again, will avoid copies in practice.static MyObject something = []() { return MyObject(...).x(...); }();
Final Takeaway
- For your current VS2017/C++14 setup: Trust NRVO (VS2017 handles it well), but use
std::moveor temporary returns if you want to eliminate any possible copy risk. - Once you migrate to C++17: You don’t need to do anything extra—mandatory elision ensures no copies or moves occur, and the code will be both safe and efficient.
内容的提问来源于stack exchange,提问作者Mr. Boy

