Move Semantics: The Gift That Keeps Taking
So C++11
gave us move semantics to make things faster, right? Just slap std::move
everywhere and watch your code go brrr. Except… not really.
Here’s the thing that drives me nuts: std::move
doesn’t actually move anything. It’s just a fancy cast. You can std::move
from a const object and nothing happens:
1const std::vector<int> data = {1, 2, 3, 4, 5};
2auto stolen = std::move(data); // Surprise! This copies
But wait, it gets better. Sometimes your “moved-from” objects are in a valid but unspecified state. Sometimes they’re empty. Sometimes they’re… exactly the same as before? The standard basically shrugs and says “figure it out.”
1std::string s = "hello world";
2auto s2 = std::move(s);
3// Is s empty now? Maybe! Maybe not! Who knows!
4// (It probably is, but the standard won't promise)
And my personal favorite: when you think you’re being clever with perfect forwarding:
1template<typename T>
2void wrapper(T&& arg) {
3 // Oops, forgot std::forward, now everything copies
4 some_function(arg);
5}
The feature that was supposed to make C++ faster by default actually requires you to memorize a bunch of arcane rules about value categories, reference collapsing, and when the compiler will elide copies anyway. Half the time your careful std::move
calls are pessimizations because the compiler would’ve done something smarter without them.
Peak C++: making performance require a PhD in type theory. Maybe something to think about after my HEP PhD.