3 ms·
The gratuitous allocation is what would need to take place if C++ didn’t have move semantics. The whole point of that part of the article is to set the backgrou
by ninkendo 4y ago
The gratuitous allocation is what would need to take place if C++ didn’t have move semantics. The whole point of that part of the article is to set the background for why C++11 move semantics were added, and what problem they solved (mainly that if C++ only allows copy and destruct, there’s no opportunity to avoid extra allocations/deletions when doing something like sorting a vector<string>.)
Also “emplace” was added to C++11 and was only possible due to the new move semantics (aka rvalue references.)
Anyhow, after explaining the background, the article goes on to critique some of the more annoying aspects of C++11’s move semantics, namely that the destructor is still called on the moved-from object (and thus you have to arrange for it to happen gracefully in your move-constructor’s logic), and that C++ lets you continue using a moved value and the compiler won’t/can’t complain to you, e.g.
// given
void foo(std::string &&s);
auto s = std::string("Hello");
foo(std::move(s));
std::count << s << std::endl; // U.B.. But the compiler won’t complain!
I have to say, the “considered harmful” monicker may actually be warranted here. When I was reading about move semantics back when it was still called C++0x, I was actually shocked to learn that there’s nothing the compiler could do to stop you from just continuing to use a reference that was moved from. That could have been one of the best benefits of the new semantics, and they just swept it under the rug and called it “undefined behavior”.
- tomjakubowski 4y agoUsing a moved-from std::string value, or one of any standard library type, isn't undefined behavior.
- ninkendo 4y ago(/me does some quick googling) I guess it’s in an “unspecified” state after move, which I suppose is different from undefined. Still doesn’t take away from my main point which is that C++ has no problem allowing you to use a reference that has been moved from (which I’d wager is a mistake in very nearly 100% of cases.)
- AnimalMuppet 4y agoIsn't that the difference between move and copy? Copy means the original is still valid; move means it isn't.
- ninkendo 4y agoWell, that’s just the rub: It’s up to the implementor of a given type to decide what happens to an object that’s been moved-from. They could do something silly like make a `print(std::string&&)` function which doesn’t actually do anything to the passed-in string, just prints it, but requires callers to `std::move` the arguments, even if it’s not actually moving anything. To C++, there’s no such thing as “moving” per se, there’s just “rvalue references”, and a standard library macro called “std::move” which is really just a static_cast to an rvalue reference. The compiler doesn’t mind if you continue to use something after casting it via std::move. It’s all rather disappointing, really (although I can understand why they did it… backwards compatibility is important in the C++ world.)
- AnimalMuppet 4y ago> the destructor is still called on the moved-from object Wait, what? If I have a class with a pointer to some allocated memory, then the constructor should allocate the memory, the destructor should free it, copy should allocate an equal-sized amount of memory, and move should just give the destination the pointer (meaning that the destination now owns the allocated block, the original does not). Calling the destructor on the original means that the destination's block of memory is freed (unless you null the origin's pointer as part of the move). I've never actually written a move that I recall, but this behavior is very surprising to me.
- ninkendo 4y agoYup, that’s why the move constructor for (e.g.) std::string has to call something like `source.buffer = nullptr;`… to prevent the source’s destructor from freeing the storage that was moved. The article covers this point pretty well.
- AnimalMuppet 4y agoTIL.