Skip to content
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

Closed
AlisdairM opened this issue Jun 10, 2016 · 5 comments · Fixed by #1867
Closed
Assignees

Comments

@AlisdairM
Copy link
Contributor

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.

@jensmaurer
Copy link
Member

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?
Otherwise generally agreed with the direction.

@AlisdairM
Copy link
Contributor Author

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.

@zygoloid
Copy link
Member

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,
17.5.2.3 [objects.within.classes] allows an implementation to provide additional non-static class members in addition to those described in the class synopsis, thus affecting what would be generated by default, and
17.6.5.12 [res.on.exception.handling] doesn't appear to place any restrictions on the exception specification of an implicit special member, or indeed on what they might throw, unless there is a Throws: paragraph or an explicitly specified noexcept.

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:

struct bad_exception : exception {
  string what_ = "bad exception";
  const char *what() const noexcept { return what_.c_str(); }
};

but it no longer has a noexcept default constructor (or copy or move constructor), and in fact the default constructor might throw.

@cpplearner
Copy link
Contributor

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.

@jensmaurer
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants