10
votes

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?

3
If I recall correctly, VS didn't implement ` = delete;` syntax until VS2013, so when you say "In every previous Visual C++ compiler I used (up to 2013), that has never been a problem", do you mean "In VS2013, that was not a problem"?cpplearner
Before VS2013, I simply used a copy constructor declaration without implementation so that it would lead to a linker error if it were actually used.fschoenm

3 Answers

9
votes

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.

7
votes

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.

0
votes

Maybe previously it managed to select implicitly generated move constructor for this, but in the VS 2015 the implicit generation of move constructor is blocked by the presence of copy operation(s), which if I recall correctly is standard-compliant behaviour.

So you simply need to define the move constructor yourself, possibly a =default one.