Move operations should be noexcept (cpp:S5018)

Hi Community,
I have a question regarding the rule cpp:S5018: move operations should be noexcept.

We see it from time to time in our codebase whenever we compose our own data structure from std containers (mostly std::map, but also std::list is not noexcept movable).

So if I have in the simplest case:

struct A {
std::map<int, X> entries;
}

the rule would flag it. I can’t think of a proper solution though to make the move operation noexcept.

On the other hand, forbidding the move operation seems to be a drastic intervention as well. If the STL data structures don’t comply to this advice from the rule description: “If you can not implement your move operations so that they never throw, you may only provide copy operations that will be safer to use.”, why should I try to do so in my custom code? In these cases I stick with the rule-of-zero advice and set the sonar issue to won’t fix.

Curious though to hear how other users solve these situations.

I’m currently on sonarqube server v2025.1.1.

Hi @jsinge,

Thanks for your message. Another option to setting the issue to “won’t fix”, would be to implement the rule-of-five and make the special member functions noexcept = default. Some implementations of std::map are not noexcept movable due to allocating an end node. Marking the function as noexcept causes std::terminate to be called instead of throwing std::bad_alloc.

Doing this allows your class to be moved instead of copied when std::vector resizes its buffer.

Yes noexcept = default; would be a pragmatic alternative, still I don’t have a good feeling about it, due to the std::terminate implication.

The hint that this is relevant for resizing std::vector is helpful, thanks!

1 Like

I’m currently running into the same issue (on MSVC) and I see three options:

  1. Implement the rule-of-five for every class that has a non-noexcept moving std container as a member. This is tedious, error prone, and contradicts the rule-of-zero.
  2. Explicitly default the special member functions and make the move operations noexcept. This is not an option for us as we cannot afford the risk of std::terminate being called.
  3. Mark the issue as accepted and move on.

We have gone for the last option whenever we encounter this issue. Am I still missing a good alternative? Maybe this rule should make an exception for standard library types? I don’t like to just mark issues as accepted, but I feel like I have no choice in this case.

I agree the solutions are not ideal. In your case I agree option 3 is the best.

Excluding Standard library types may be incorrect:

  • Excluding them unconditionally is incorrect since Standard library types may depend on user-defined types. When using an std::map<int, int, std::less<int>, my_allocator> object and my_allocator can throw when being moved, then an exception can be thrown when moving the object.
  • The C++ Standard allows Standard libraries to give stronger noexcept guarantees than the Standard provides. This means using the noexcept status of the Standard library used may give an incorrect result. For example, moving an std::map<int, int> object with MSVC STL may throw an exception. When using libc++ it will never throw. When you ship your software for both Windows (MSVC STL) and Apple (libc++) and test your code on Apple, then there would no issue be detected. There may be a performance penalty on Windows.
1 Like