In a probject I used code similar to the following:
class C {
public:
C() {}
C(const C&) = delete;
};
C f() {
return C();
}
int main() {
f();
}
In every previous Visual C++ compiler I used (up to 2013), that has never been a problem. But when I try to compile it with the new Visual C++ 2015 compiler I get the following error:
1>c:\devel\c++11\playground\main.cpp(10): error C2280: 'C::C(const C &)': attempting to reference a deleted function
1> c:\devel\c++11\playground\main.cpp(6): note: see declaration of 'C::C'
I'm not sure why it previously worked but I assume that because of return value optimization the default constructor was called and not the copy constructor.
Is the code I used even legal C++? And if not, what would be the correct way of implementing this code without requiring a copy constructor for my class C
? I could of course use a move constructor but then I assume the code would have never been valid C++ before C++11?
In the context of the C++ programming language, return value optimization (RVO) is a compiler optimization that involves eliminating the temporary object created to hold a function's return value. RVO is allowed to change the observable behaviour of the resulting program by the C++ standard.
RVO is a compiler technique to avoid copying objects when the object is returned as function value. This optimization helps a function to efficiently return large objects while also simplifying the function's interface and eliminating scope for errors.
Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.
> Note also that C doesn't have return-value-optimization, hence all your struct-returning functions will cause a call to memcpy (won't happen when compiled in C++ mode of course).
Function arguments and return values are initialized using copy-initialization. Copy-initialization requires copy constructors to be accessible, even if they are elided by (N)RVO:
If T is a class type, and the type of other is different, or if T is non-class type, but the type of other is a class type, user-defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type and a conversion function is available) are examined and the best one is selected through overload resolution. The result of the conversion, which is a prvalue temporary if a converting constructor was used, is then used to direct-initialize the object. The last step is usually optimized out and the result of the conversion is constructed directly in the memory allocated for the target object, but the appropriate constructor (move or copy) is required to be accessible even though it's not used.
You need to follow the rule of 5. Since you deleted the copy constructor, you need to define your move constructor.
C(C&&) = default;
C& operator=(C&&) = default;
With move constructor - works
Without move constructor - doesn't work, violates rule of 5
Note the above site uses gcc and even it will not compile, so it is not specific to Visual Studio, this is a defined and expected behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With