Search code examples
c++move-semanticsnoexcept

Does noexcept matter with explicitly defaulted move constructor/assignment operator?


class C
{
    public:
        C(C&&) = default; // (1)
        C& operator=(C&&) = default; // (1)

        C(C&&) noexcept = default; // (2)
        C& operator=(C&&) noexcept = default; // (2)
}

As far as I know, if move constructor/assignment operator are either implicitly generated or explicitly defaulted by user (1), the compiler will decide whether these special member functions should be noexcept or not depending on whether all the members of a class provide noexcept guarantees for move operations. But what if I want to use default move special members and force them to be noexcept regardless of the underlying members exception guarantees for move operations? Is there any difference for the compiler between (1) and (2) declarations?


Solution

  • Ok, I found the answer in Nico's Josuttis book "C++ Move Semantics - The Complete Guide":

    When you have a defaulted special member function you can explicitly specify a different noexcept guarantee than the generated one. For example:

    class C
    {
      ...
     public:
      C(const C&) noexcept = default;     // guarantees not to throw (OK since C++20)
      C(C&&) noexcept(false) = default;   // specifies that it might throw (OK since C++20)
      ...
    };
    

    Before C++20, if the generated and specified noexcept condition contradict, the defined function was deleted.

    I asked this question because I want STL algorithms to always apply move semantics to my classes regardless of their members' exception guarantees, and I am ready to deal with std::abort if something goes wrong. But it seems that prior to C++ 20 the only way to force noexcept guarantees on move special member functions was to explicitly define them, even if you don't need your own implementation. It turns out C++20 solved this by allowing us to specify noexcept guarantees for defaulted special member functions. Of course, as in case with any other noexcept function, if any of the members of a class throws during a move operation, std::abort will be called if move constructor/assignment operator are declared noexcept for that class, as it was kindly pointed out by @MichaëlRoy in the comments section.