class Object
{
int* data = new int[32];
void move_into( Object& object )
{
object.data = data;
data = nullptr;
}
~Object()
{
delete[] data;
}
}
Object a;
Object b;
a.move_into( b );
I think much of the confusion arises from wondering where is the allocation for int* data located? It's somewhere on the heap. What if the data member were int data[32] instead? What would the class look like? What would 'a' look like afterwards?Move, simply
11–20 of 96 posts
Re: Move, simply
#12I get the impression that the question-and-answer section is circling around the issue of the semantic status of a moved-from variable without quite dispatching it. Is a variable, when it is moved from and thus (if implemented properly) in a valid though unspecified state, not semantically in the same sort of state as a constructed but uninitialized integer, where any bit pattern is valid, but if it happens to be zer…
C++ is designed to be low overhead. Requiring a moved-from object to take on a newly-constructed state will add overhead that won't be necessary most of the time. If you want to add your own convention you're free to do so, knowing what the downside will be.
Q: Does “but unspecified” mean the only safe operation on a moved-from object is to call its destructor?
A: No.
...
Q: What about objects that aren’t safe to be used normally after being moved from?
A: They are buggy...
Re: Move, simply
#13I found it's much easier to understand the concepts behind move operations if you write an object that implemented move semantics by itself. class Object { int* data = new int[32]; void move_into( Object& object ) { object.data = data; data = nullptr; } ~Object() { delete[] data; } } Object a; Object b; a.move_into( b ); I think much of the confusion arises from wondering where is the allocation for int* data located…
Re: Move, simply
#14I found it's much easier to understand the concepts behind move operations if you write an object that implemented move semantics by itself. class Object { int* data = new int[32]; void move_into( Object& object ) { object.data = data; data = nullptr; } ~Object() { delete[] data; } } Object a; Object b; a.move_into( b ); I think much of the confusion arises from wondering where is the allocation for int* data located…
Don't you have to destroy the target object, before you move into it?
But maybe with int data[32] as a data member this is harder to reason about.
This is a way to illustrate move semantics and confusion behind what happens to the 'moved from' object.
Re: Move, simply
#15Earlier quoted context omitted.
C++ is designed to be low overhead. Requiring a moved-from object to take on a newly-constructed state will add overhead that won't be necessary most of the time. If you want to add your own convention you're free to do so, knowing what the downside will be.
Indeed, though it should at least go through destruction without undesirable side-effects. In the article, however, Herb Sutter appears to be arguing for something stronger: Q: Does “but unspecified” mean the only safe operation on a moved-from object is to call its destructor? A: No. ... Q: What about objects that aren’t safe to be used normally after being moved from? A: They are buggy...
Re: Move, simply
#16The gist seems to be that newer standards simplify move semantics, so GCC introduced a warning when you write `return std::move(result);` because the manual move is redundant (and could actually slow down your code).
However, if you need your code to run on older compilers, you can get hard-errors by, for example, older versions of GCC not binding the move constructor on a return.
Also, because not all compilers have implemented the newer move semantics, you could get copies rather than moves even on newer compilers.
So while I'm sure "only call std::move in this one circumstance" will be great advice one day, in the real world at the moment the situation seems decidedly more complex.
[0]http://lists.llvm.org/pipermail/cfe-dev/2020-February/064662... (mid-thread)
Re: Move, simply
#17No, they aren't simple and the fact that are still widely misunderstood is basically proof of that.
Maybe, just maybe, it has something to do with stuffing rvalue references, perfect forwarding and the whole universal-references-template-clusterfuck into one and the same syntax. [1]
The default compiler-generated move can leave behind a null sp member
Which is precisely why std::move should be used with caution, which, in turn, is precisely why many codebases are still avoiding it. This problem in particular could have been avoided by not auto-generating move constructors.
I really want to like this feature of C++, but I feel its design is just so horribly bad and confusing that I'd feel like a total d..k if I started to force it on a team of developers (of various levels of experience) if there's no crystal clear need for it (and let's be honest, C++ was already pretty successful in getting shit done before there was std::move and rvalue references).
[1] http://thbecker.net/articles/rvalue_references/section_01.ht...
Re: Move, simply
#18I found it's much easier to understand the concepts behind move operations if you write an object that implemented move semantics by itself. class Object { int* data = new int[32]; void move_into( Object& object ) { object.data = data; data = nullptr; } ~Object() { delete[] data; } } Object a; Object b; a.move_into( b ); I think much of the confusion arises from wondering where is the allocation for int* data located…
Don't you have to destroy the target object, before you move into it?
Re: Move, simply
#19Huh, I'm sure he is correct on the intent of the spec, but it is certainly not how our team has been interpreting the allowable state of objects after a move. And while I understand where he is coming from in always wanting the object to meet its invariants until it is destructed, I think this approach will make code harder to reason about rather than easier. I'm a huge proponent that classes should be fully configur…
What is "partially configured" after all? IMHO this dichotomy is just a headache brought to you by yours truly, OOP (or rather, syntactically enforced OOP). Personally I don't want to spend the rest of my life pondering such philosophical questions. Or dealing with all the consequences, like constructor exceptions, forced dynamic allocation because static doesn't work without initialization, etc, pp.
Re: Move, simply
#20Earlier quoted context omitted.
Indeed, though it should at least go through destruction without undesirable side-effects. In the article, however, Herb Sutter appears to be arguing for something stronger: Q: Does “but unspecified” mean the only safe operation on a moved-from object is to call its destructor? A: No. ... Q: What about objects that aren’t safe to be used normally after being moved from? A: They are buggy...
Surely he doesn't consider unique_ptr to be buggy, so there must be some subtlety that isn't captured by that statement. Perhaps it's in the definition of "normally" - you can't dereference a moved-from unique_ptr, but you can certainly reassign it to a new pointer and go on to use it from there.
BTW, I have updated my original post to mention swapping source and destination, another common idiom for satisfying the requirement.