Example
struct MyObject {
MyObject(int value):value(value) { }
MyObject(MyObject const&o):value(o.value) { }
int value;
};
Assume that the copy constructor does something in addition to be useful. Then
std::function<void()> f() {
MyObject o;
std::vector<int> v;
return [=]() { /* use v and o */ &o; &v; }
}
v
and o
are first copied into the initial lambda object, which is fine. But then they are again copied everytime the lambda object needs to be moved. Even though v
could be moved, but it is not. That's because the lambda does not have an implicit move constructor, because o
does not have a move constructor or trivial copy constructor.
Can someone please explain the rationale behind this?
I seem to recall that this is a compromise between the two extremes, those who did not want implicit generation of move constructors at all, and those that wanted move constructors to be generated automatically in most circumstances.
Among the people that wanted no implicit generation of move constructors, Dave Abrahams wrote an article called Implicit Move Must Go. The rationale there is that under some circumstances, even if the members are movable, the implicit generation of a move constructor can break invariants.
Early this year (2011) the committee decided to keep implicit generation of move constructors in a decision that seemed to emphasize performance boost in existing code over safety issues (1), and again Dave blogged about it. It does not talk about the specifics of the decision, the pros and cons, but is not quite happy with the result either.
Edit (from Jerry Coffin): Here's the list of conditions for implicit declaration of a move constructor:
If the definition of a class X does not explicitly declare a move constructor,
one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator,
— X does not have a user-declared destructor, and
— the move constructor would not be implicitly defined as deleted.
The basic idea is that inclusion of any of these in the class is an indication that an implicitly generated move ctor is likely to mis-behave. While that's true, the conditions in the list are neither necessary nor sufficient to the determination, so many move ctors that would have been useful aren't generated, and many that will cause problems can be generated. Worse, the rules are already long and complex enough that few remember them all, and fixing them would probably at least double that.
[end of Jerry's contribution/rant]
(1) Thanks to Gene Bushuyev for the insight as to why the decision was taken
Kinda guessing, but I suspect it may have to do with exceptions. That is, move constructors should really be noexcept, but having a move constructor call a copy constructor could have it throw.
(trying to refresh my memory from here, which I think covered this issue)
EDITED TO ADD:
And my guess was wrong. The correct answer, as far as I can tell, is from here. The presence of the copy constructor is an indication that the class has invariants, and the default generated move constructor might not respect those invariants, and therefor, should not be generated.
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