> So the standard library plays it safe: if your move constructor might throw (because you didn’t mark it noexcept), containers just copy everything instead. That “optimization” you thought you were getting? It’s not happening.
This is a bit of a footgun and clang-tidy has a check for it: performance-noexcept-move-constructor. However, I don't think it's enabled by default!
The reason performance-noexcept-move-constructor is not enabled by default is likely because blindly applying noexcept is dangerous if the underlying logic isn't actually exception-free. If you let clang-tidy slap noexcept on a move constructor that does end up throwing (perhaps because it calls into a legacy member or allocates memory internally), the runtime behavior changes from caught exception to std::terminate().
Most sensible Compiler flags aren't enabled by default... I keep a list of arguments for gcc to make things better, but even then you'll also wanna use a static analysis tool like clang-tidy
performance-noexcept-move-constructor is great but it also complains about move assignment operators, which are completely different beasts and are practically impossible to make noexcept if your destructors throw.
If I'm not mistaken, all the pitfalls in the article have clang-tidy lints to catch
Nothing about clang-tidy is enabled by default, and getting it to run at all in realistic projects is quite a chore.
Throwing move is super weird too. I believe that it was a mistake to not treat user move like C++11 destructors and default to noexcept(true) on them. But it is what it is.
On the other hand, writing special member functions at all(move & copy constructor/assignment, destructor) is a smell for types that don't just manage the lifetime of an object(unique_ptr like things). People should not generally be writing them and being open to the mistake of getting noexcept wrong.