New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exceptions classes deriving directly from std::exception should be presented consistently #748
Comments
How do you know the implicitly-declared copy constructor/assignment operator will be non-throwing? Maybe the implementation added a data member whose copy or assignment is potentially-throwing? Do we have blanket wording somewhere, or should we add this? |
If we add data members etc. we still need to conform to the observable effects of as-if. Here, we would be relying on the implicit signatures of operations that are explicitly noexcept in the base class also being noexcept. |
I don't see how that follows, given that: 17.5.2.2 [functions.within.classes] only requires the "same apparent semantics" for special members as those that would be generated by default, As a result, I don't see any restriction on the exception specification of an implicit special member in the standard library. In particular, this appears to be a valid implementation of the simplified specification:
but it no longer has a |
There is [exception]/2 (added by LWG 471) which requires that "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", but it does not constrain the default constructor, and I'm not sure whether "do not exit with an exception" implies that these functions have a non-throwing exception specification. |
In general, the requirement "does not exit via an exception" does not imply a non-throwing exception specification. But we could certainly massage these words to simply require a non-throwing exception specification. |
I am opening an issue, as part of resolving this will be adding the results of the 'what' function for each class below to the index of implementation defined behavior. However, before looking into that, I want to ensure a consistent presentation of these classes with essentially the same behavior.
bad_alloc
bad_array_new_length
bad_cast
bad_exception
bad_function_call
bad_typeid
bad_weak_ptr
At one extreme, we have the specification for the original bad_* classes:
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;
};
At the other, we have the specification for the exceptions adopted via TR1:
class bad_weak_ptr: public std::exception {
public:
bad_weak_ptr() noexcept;
};
Note that an override of 'what' is assumed, but not declared, with the specification for 'what' buried in the contract of the default constructor.
My preferred formulation would be:
struct bad_exception : exception {
const char* what() const noexcept override;
};
This strips out the redundant exceptions and assignment operators, which will be implicitly declared/defined with 'noexcept' specifier. It uses 'struct' as defaulting everything to public access simplifies further, and finally adds the 'override' qualifier to 'what', for completeness. As this involves a change of normative wording, I believe it would require an LWG issue to resolve.
Failing that, I would prefer to consistently apply the 'bad_alloc' format above, where everything is called out explicitly. In both cases, we get a 'what' function in the index that can be linked to the index of implementation defined behavior.
The text was updated successfully, but these errors were encountered: