Search code examples
c++exceptionc++11copy-constructornoexcept

Should I declare the copy constructor of my exceptions noexcept?


In More Effective C++, Scott Meyers says

C++ specifies that an object thrown as an exception is copied.

I suppose then, that if the copy constructor throws an exception in turn, std::terminate is called, so this is a good reason for declaring all my exceptions' copy constructors noexcept (and also, I guess, to not throw objects which allocate memory from the heap, like std::string).

Yet I was surprised to see that the standard library implementation shipped with GCC 4.7.1 doesn’t define those copy constructor for std::bad_alloc and std::exception. Shouldn’t they define them noexcept?


Solution

  • Section 18.8.1 [exception]/p1 specifies:

    namespace std {
        class exception {
        public:
          exception() noexcept;
          exception(const exception&) noexcept;
          exception& operator=(const exception&) noexcept;
          virtual ~exception();
          virtual const char* what() const noexcept;
      };
    }
    

    I.e. the copy constructor and copy assignment of std::exception shall be noexcept, and this is testable with:

    static_assert(std::is_nothrow_copy_constructible<std::exception>::value, "");
    static_assert(std::is_nothrow_copy_assignable<std::exception>::value, "");
    

    I.e. if an implementation does not make these members noexcept, then it is not conforming in this regard.

    Similarly, 18.6.2.1 [bad.alloc]/p1 also specifies noexcept copy:

    namespace std {
           class bad_alloc : public exception {
           public:
             bad_alloc() noexcept;
             bad_alloc(const bad_alloc&) noexcept;
             bad_alloc& operator=(const bad_alloc&) noexcept;
             virtual const char* what() const noexcept;
      };
    }
    

    Additionally, all of the std-defined exception types have noexcept copy members, either explicitly, or implicitly. For the types defined in <stdexcept> this is usually implemented with a reference-counted buffer for the what() string. This is made clear in [exception]/p2:

    Each standard library class T that derives from class exception shall have a publicly accessible copy constructor and a publicly accessible copy assignment operator that do not exit with an exception. ...

    That is, in a quality implementation (and it does not take heroics to create a quality implementation in this regard), not only will the copy members of exception types not throw an exception (naturally because they are marked noexcept), they also will not call terminate().

    There is no failure mode for copying the std-defined exception types. Either there is no data to copy, or the data is reference counted and immutable.