C++ās greatest innovation is RAII, āResource Acquisition is Initialization.ā Despite its name, RAII has little to do with initialization, but rather destruction. In a RAII paradigm, an objects lifetime is tied to its scope, and its destructor is called when the scope ends. This is immensely helpful, as anyone who has seriously written C can tell you that a solid 90% of C code is just cleanup.
Unfortunately, C++ās ability to be wrong about everything means that it also suffers a big footgun with the lack of destructive moves.
Now, Iām gonna tell you the reason this surprised me is that Iām really used to Rust these days. The concept of a ānon-destructiveā move doesnāt really make sense to me, since Iām used to move semantics⦠moving data.
Hereās a toy example of the problem:
#include <iostream>
class MyVeryCoolObject {public: MyVeryCoolObject() { std::cout << "I am initialized!" << std::endl; } ~MyVeryCoolObject() { std::cout << "I have been destroyed :(" << std::endl; }};
int main() { auto verycoolobject = MyVeryCoolObject(); { auto coolerobject = std::move(verycoolobject); } return 0;}
You may think the answer to this question is obvious:
I am intialized!I have been destroyed :(
Since we moved the data, our verycoolobject
no longer exists, its āgutsā have been moved into the coolerobject
. Then, at the end of the scope we created, coolerobject
, which had the guts of the original object, will call its destructor. Then, at the end of the main block, since myverycoolobject
is, uh, nothing, it will simply never call its destructor because it doesnāt exist. Right. Right?
Hereās what it actually outputs, compiled with Apple clang version 15.0.0 (clang-1500.3.9.4)
.
I am intialized!I have been destroyed :(I have been destroyed :(