4 ms·
> 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. Th
by krona 9mo ago
> 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!
- juliangmp 9mo agoMost 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
- teraro 9mo agoWould you mind sharing your list?
- juliangmp 9mo agoSure, I put quickly put them into a small markdown file. At my job we have a cmake interface target that handles these (along with some version checks and project specific stuff), but I can't publish that of course. I might put these into a cmake file at some point, not sure. https://codeberg.org/JulianGmp/sane-args-cpp https://codeberg.org/JulianGmp/sane-args-cpp
- Fiveplus 9mo agoThe 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().
- phkahler 9mo agoExceptions should never be enabled by default. We live in a 64bit world so allocations failing indicates some other problem.
- zbentley 9mo agoWhat does processor but width have to do with the likelihood of allocation failures?
- petcat 9mo ago640K ought to be enough for anybody!
- HarHarVeryFunny 9mo agoI think what he means is that on a 64-bit system you have a massive virtual address space (typically only 48-bit, but that's still 256TB), and since malloc allocates from virtual address space, not limited by physical memory, it is unlikely you will get a malloc failure (unless you are trying to allocate more than 256TB per process, maybe due to a memory leak).
- locknitpicker 9mo ago> it is unlikely you will get a malloc failure That assertion completely misses the point. The scenarios involving move constructors throwing exceptions involve objects being stuck in an inconsistent/zombie state. In fact, the whole point of a move constructors is to avoid having to allocate memory.
- zbentley 9mo agoThat's not true as generally as you might expect. Folks might be using mmap with MAP_POPULATE; they might have overcommit turned off; they might be operating in an rlimit/cgroup (like most container runtimes/orchestrators configure) that limits memory; they might be on a system which doesn't have virtual memory (plenty such systems exist in 64-bit architectures); they might be using calloc on an OS that zeros pessimistically/early; and so on.
- rfc3092 9mo agoperformance-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.
- dataflow 9mo agoIf that's the issue you're facing, consider clang-query, e.g.: https://godbolt.org/z/bfG94qGan https://godbolt.org/z/bfG94qGan match cxxConstructExpr(hasDeclaration(cxxConstructorDecl(isMoveConstructor(), unless(isNoThrow())).bind("throwing-move"))) You can put extra constraints on the caller if you'd like (e.g., isInStdNamespace()), though it's less trivial. Happy to help write something if you have a precise idea of what you want to match.
- beached_whale 9mo agoThrowing destructors will generally end in termination of the program if they are used as class members. Types like scope_exit are fine, but anywhere else will probably have noexcept(true) on it's destructor.
- deleted 9mo ago[deleted]
- beached_whale 9mo agoThrowing 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.
- locknitpicker 9mo ago> 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. I think you're missing a fair deal of insight into the issue. The move semantics proposal documents this aspect in clear and unambiguous terms: - Almost any class should be able to create a nothrow move assignment operator. - a basic requirement is that a class must have a valid resource less state (i.e., remain in a valid state after having been moved) - those that can't, shouldn't define move semantics.
- grogers 9mo agoIf I'm not mistaken, all the pitfalls in the article have clang-tidy lints to catch
- jeffbee 9mo agoNothing about clang-tidy is enabled by default, and getting it to run at all in realistic projects is quite a chore.