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:
const std::vector<int> data = {1, 2, 3, 4, 5};
auto 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.”
std::string s = "hello world";
auto s2 = std::move(s);
// Is s empty now? Maybe! Maybe not! Who knows!
// (It probably is, but the standard won't promise)
And my personal favorite: when you think you’re being clever with perfect forwarding:
template<typename T>
void wrapper(T&& arg) {
// Oops, forgot std::forward, now everything copies
some_function(arg);
}
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.