All of GCC 4.8.4, 4.9.3, 5.3.0 pass the tests for std::exception
(for any of -std=c++11/1y/14/1z/17 options, where available):
static_assert(std::is_nothrow_copy_constructible<std::exception>::value, "test exception");
static_assert(std::is_nothrow_copy_assignable <std::exception>::value, "test exception");
Which is fine, since std::exception
has noexcept special members (C++14 18.8.1):
namespace std { class exception { public: exception() noexcept; exception(const exception&) noexcept; exception& operator=(const exception&) noexcept; virtual ~exception(); virtual const char* what() const noexcept; }; }
Unfortunately, all of the compilers above fail on the following static_assert
s:
static_assert(std::is_nothrow_copy_constructible<std::runtime_error>::value, "test runtime_error");
static_assert(std::is_nothrow_copy_assignable <std::runtime_error>::value, "test runtime_error");
The standard contains only the following about std::runtime_error
in 19.2.6:
namespace std { class runtime_error : public exception { public: explicit runtime_error(const string& what_arg); explicit runtime_error(const char* what_arg); }; }
But nothing is said about the noexcept
ness of the other (implicitly declared special) members nor the storage implementation requirements of what_arg
.
The standard (C++14) says the following in 15.4/14:
An inheriting constructor (12.9) and an implicitly declared special member function (Clause 12) have an exception-specification. If f is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f allows all exceptions if any function it directly invokes allows all exceptions, and f has the exception-specification noexcept(true) if every function it directly invokes allows no exceptions.
And the following in 18.8.1/2:
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.
Since std::runtime_error
does not expose the implementation of the what_arg
storage, we do not know whether it's (special) members are noexcept or not, so the noexceptness of std::runtime_error
's copy constructor or copy assignment members are undecidable. Our only bet is 18.8.1 above.
Question 1/a) We consider std::runtime_error
's copy constructor or copy assignment to be noexcept (1, 2). Is this true / state-of-the-art / best practice?
Question 1/b) Do not we need to explicitly state this in the standard? (Like in 18.8.2, Class bad_exception
)
Question 1/c) Is it a bug in GCC that it fails the static_assert tests above?
Question 2) If the above deduction is wrong, could someone point me to the section(s) in the standard which state that std::runtime_error has noexcept copy constructor (and copy assignment)? (Or where it states that they are not.)
The difference between a copy constructor and an assignment operator is that a copy constructor helps to create a copy of an already existing object without altering the original value of the created object, whereas an assignment operator helps to assign a new value to a data member or an object in the program.
Copy constructor. Assignment operator. It is called when a new object is created from an existing object, as a copy of the existing object. This operator is called when an already initialized object is assigned a new value from another existing object. It creates a separate memory block for the new object.
However, the copy constructor for an exception object still must not throw an exception because compilers are not required to elide the copy constructor call in all situations, and common implementations of std::exception_ptr will call a copy constructor even if it can be elided from a throw expression.
std::runtime_errorDefines a type of object to be thrown as exception. It reports errors that are due to events beyond the scope of the program and can not be easily predicted.
Consider LWG 1371:
None of the exception types defined in clause 19 are allowed to throw an exception on copy or move operations, but there is no clear specification that the operations have an exception specification to prove it. Note that the implicitly declared constructors, taking the exception specification from their base class (ultimately
std::exception
) will implicitly generate anoexcept
exception specification if all of their data members similarly declarenoexcept
operations. As the representation is unspecified, we cannot assume nonthrowing operations unless we explicitly state this as a constraint on the implementation.[ Resolution proposed by ballot comment: ]
Add a global guarantee that all exception types defined in clause 19 that rely on implicitly declared operations have a non-throwing exception specification on those operations.
In the 2010 Batavia meeting, it was found that [exception]/2 "covered this":
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.
Therefore it still isn't mandated that these special member functions are noexcept
. And according to the way that implicit exception specifications are determined in [except.spec]/16, since an implementation can add both arbitrary parameters with default arguments and members, it's implementation-specific whether or not these special member functions are noexcept
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With