Is there complete list (maybe recursively defined) of "code statements" that can lead to exception in c++? Something like this:
1) throw statement (naturally)
2) calls to new
3) calls to any functions from standard library that are documented to be able to throw.
4) calls to any user-defined functions (including constructors) that contain operations from 1-3.
5) Something else? Allocating local objects on stack, operations on built-in types, dereferencing pointers, type casts - are they able to throw?
6) Everything else is exception-free.
By exception-free I don't mean operations that are always successful. Dereferencing a pointer surely isn't. But still it does not make sense to wrap it in try-catch block, think about exception-safety of function dereferencing a pointer, etc. So the code that either successful or leads to undefined behaviour can be considered as exception-free.
Upd. Despite my last paragraph I still got a comment that undefined behaviour also can throw, so let me explain what I mean. Consider following code:
void bar();
Class C{
...
public:
foo() {
something_that_breaks_class_invariants;
bar();
something_that_restores_class_invariants;
}
}
If I correctly understand what exception safety is about, then if bar() can throw exception, this code is bad. I should change the order of statements, or I should wrap bar() in try-catch block, restore class invariants and propagate exception further.
But if bar() either successfully returns or leads to undefined behaviour (because, I don't know, something else is broken), then foo() is ok. foo() can't do anything and shouldn't care about possible undefined behaviour of bar(). In this sense bar() is exception-free, can be marked noexcept, etc.
So my question is: what kinds of statements can be in bar() to consider it exception-free is this sense?
Yes, the list of things that can throw in C++ can be exhaustively defined.
throw expression
new can throw bad_alloc
dynamic_cast can throw bad_cast
typeid can throw bad_typeid
any call to a function that is not noexcept or throw()
The last point also applies to all the implicit function calls of C++: default/copy/move constructors, overloaded operators, destructors (note that those default to noexcept) and conversion operators.
If you are ever in doubt about a particular expression, you can use the noexcept operator to have the compiler tell you whether it's theoretically possible for the expression to throw.
Your list is almost complete. There's indeed one cast that can throw: dynamic_cast<Derived&>. That's because there are no null references. dynamic_cast<Derived*> returns a null pointer instead of throwing.
In all cases, exceptions are thrown when evaluating expressions. Variable definitions may only throw when they contain an expression, e.g. in an initializer or inside a constructor.
The "Undefined Behavior" bit is a red herring. You simply can't reason about a C++ program that has Undefined Behavior, not even about what might have happened prior to that UB. That means we're assuming no UB whenever we are reasoning about defined C++ behavior.
The answer is simple: Any statement that uses an overloadable operator or function that cannot be proven not to throw, can throw. Here are some examples:
template <class T> T foo(const T& arg) { return arg; } //can throw (copy constructor!)
template <class T> void foo(T a, T b) { a+b; } //can throw (+ is overloadable)
template <class T>
void foo(T iter, T end) {
for(; iter < end; iter++) { //both the < and the ++ operator can throw
iter->bar(); //even the -> operator is overloadable and can throw
}
}
In short, whenever you do not have knowledge of the involved types, as is generally the case with templates, you pretty much have to call any statement a throwing statement.
Afaik, the only valid defense against this not to allow exceptions in the first place.
Related
The c++ reference for std::accumulate does not mention any exception to be possibly thrown by std::accumulate, still its definition does not contain noexcept. Assuming one uses types and operations which do not throw, is safe to employ std::accumulate in a member function declared as noexcept, or does one incur in UB?
For example:
#include <iostream>
#include <numeric>
#include <vector>
class A {
std::vector<int> m_v;
public:
A(std::size_t N) : m_v(N, 1) { }
int sum() const noexcept { return std::accumulate(m_v.begin(), m_v.end(), 0); }
};
int main()
{
A x{3};
std::cout << x.sum() << std::endl;
return 0;
}
Is declaring A::sum() as noexcept a source of UB?
There are preconditions on std::accumulate(), e.g., that the end of the range is reachable from the begin of the range. So far the standard library hasn’t put noexcept on functions with preconditions (at least, not in general; there may be special cases) as a debugging implementation could assert that there is a problem, e.g., by throwing an exception: what happens when undefined behavior is triggered is undefined and implementations are free to define any of that if they wish to do so.
Also, any of the functions called by std::accumulate() are allowed to throw, i.e., any noexcept declaration would need to be conditional. I think it is unlikely that the algorithms will get corresponding noexcept declarations in general.
As the specification doesn’t mention any case where std::accumulate() throws when called within contract, it won’t throw if none of the called operations throws.
Yes, in general it can.
First of all the operations that are customizable through the template arguments and that std::accumulate must perform can throw.
But even aside from that the standard does allow an implementation to throw implementation-defined exceptions if a standard library function does not have a non-throwing exception specification and the description of the function doesn't say otherwise, see [res.on.exception.handling]/4.
That being said, I would be surprised if the use of std::accumulate in your example would throw. There is no dynamic allocation required, so a potential throwing of std::bad_alloc should not be necessary and that is the most likely candidate for implementation-defined exceptions. The summation, copy and/or move operations on ints are also non-throwing.
In any case adding noexcept to a function does not cause undefined behavior if an exception is thrown inside of it. Instead it is well-defined that, should an exception reach the noexcept function's outer scope, std::terminate will be called, which by default abort the program, but can be customized to some degree.
The pop() method of std::priority_queue is not declared noexcept, so in theory could throw an exception. But when might it throw an exception, and what might those exceptions be?
It could be marked nothrow, but isn't.
Why std::priority_queue::pop could* not throw
void pop();
Removes the top element from the priority queue. Effectively calls
std::pop_heap(c.begin(), c.end(), comp); c.pop_back();
c is by default an std::vector.
[vector.modifiers]/4&5
void pop_back();
4/ Complexity: The destructor of T is called the number of times equal to the number of the elements erased, but the assignment operator of T is called the number of times equal to the number of elements in the vector after the erased elements.
5/ Throws: Nothing unless an exception is thrown by the assignment operator or move assignment operator of T.
*So only the destructor of T is called and that one cannot throw because of
[requirements.on.functions]/2.4
2/ In particular, the effects are undefined in the following cases:
[...]
2.4/ if any replacement function or handler function or destructor operation exits via an exception, unless specifically allowed in the applicable Required behavior: paragraph.
Why is std::priority_queue::pop not nothrow?
Since an exception thrown from T::~T would lead to UB, the implementation can assume it cannot happen and still conform to the Standard. Another way to deal with it is to let such library functions nothrow(false) and not dealing with it.
I have an operator[] for my class and all it does is call std::unique_ptr::operator[] on a unique_ptr member. The relevant part is just this:
template <typename T> struct Foo {
T& operator [](const size_t pos) const noexcept
{
return data_[pos];
}
std::unique_ptr<T[]> data_;
};
I've marked the operator as noexcept. However, unique_ptr::operator[] is not noexcept. I am unable to find out why that is, and whether I can just assume that it will never throw. unique_ptr::operator[] itself does not list any exceptions in the documentation (cppreference and MSDN claim it does not define any list of exceptions it might throw.)
So I assume the missing noexcept might either be: a) a mistake, or b) the underlying datatype accessed by the operator might throw. Option a would be nice, since that would mean I can mark my own operator noexcept. Option b would be difficult to understand, since all the operator does it get a reference and it doesn't call anything.
So, long story short, is there any possibility of unique_ptr::operator[] ever throwing, and is it safe to call it from a noexcept function?
So, long story short, is there any possibility of unique_ptr::operator[] ever throwing
Yes. It will simply use [] on the pointer type that it has. And that could throw. Recall that, thanks to deleter gymnastics, the pointer type need not be an actual pointer. It could be a user-defined object type with its own operator[] overload that could throw on out-of-bounds use.
I thought I give it a try (I am not sure of the answer).
I went through the noexcept reference and my understanding is that noexcept is simply indicating that the function should not throw exception, so that the compiler could make more agressive optimisations.
As to why the unique_ptr::operator[] is not noexcept I guess that the one standardising imagined that some implementors may throw and some other may not.
My opinion is that unique_ptr::operator[] may be throwing depending on the implementation of unique_ptr (typically when the index is out of bound).
However depending of the context of your code you could make sure this won't happen and decide to specify noexcept for your operator[].
Given this statement (which, as a sidenote, is not my preferred coding style)
if( doSomething() ) {}
Does 'the C++ Standard' guarantee that the function is called?
(It's return value has no effect on the execution path, so the compiler
may follow the ideas of shortcut evaluation and optimize it away.)
There's no short-circuit operator involved, so the function is guaranteed to be called if it can't be optimized away without removing side-effects. Quoting the C++11 standard:
[...] conforming
implementations are required to emulate (only) the observable behavior of the abstract machine as explained
below.5
5 This provision is sometimes called the “as-if” rule [...] an actual implementation need not evaluate part of an expression
if it can deduce that its value is not used and that no side effects
affecting the observable behavior of the program are produced.
So, something like
int doSomething() { return 1; }
might be optimized away, but
int doSomething() { std::cout << "d\n"; return 1; }
isn't allowed to.
Additionally, since C++11, you can write more sophisticated functions and still make them evaluated at compile time by using constexpr.
In the C++ standard draft (N3485), it states the following:
20.7.1.2.4 unique_ptr observers [unique.ptr.single.observers]
typename add_lvalue_reference<T>::type operator*() const;
1 Requires: get() != nullptr.
2 Returns: *get().
pointer operator->() const noexcept;
3 Requires: get() != nullptr.
4 Returns: get().
5 Note: use typically requires that T be a complete type.
You can see that operator* (dereference) is not specified as noexcept, probably because it can cause a segfault, but then operator-> on the same object is specified as noexcept. The requirements for both are the same, however there is a difference in exception specification.
I have noticed they have different return types, one returns a pointer and the other a reference. Is that saying that operator-> doesn't actually dereference anything?
The fact of the matter is that using operator-> on a pointer of any kind which is NULL, will segfault (is UB). Why then, is one of these specified as noexcept and the other not?
I'm sure I've overlooked something.
EDIT:
Looking at std::shared_ptr we have this:
20.7.2.2.5 shared_ptr observers [util.smartptr.shared.obs]
T& operator*() const noexcept;
T* operator->() const noexcept;
It's not the same? Does that have anything to do with the different ownership semantics?
A segfault is outside of C++'s exception system. If you dereference a null pointer, you don't get any kind of exception thrown (well, atleast if you comply with the Require: clause; see below for details).
For operator->, it's typically implemented as simply return m_ptr; (or return get(); for unique_ptr). As you can see, the operator itself can't throw - it just returns the pointer. No dereferencing, no nothing. The language has some special rules for p->identifier:
§13.5.6 [over.ref] p1
An expression x->m is interpreted as (x.operator->())->m for a class object x of type T if T::operator->() exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3).
The above applies recursively and in the end must yield a pointer, for which the built-in operator-> is used. This allows users of smart pointers and iterators to simply do smart->fun() without worrying about anything.
A note for the Require: parts of the specification: These denote preconditions. If you don't meet them, you're invoking UB.
Why then, is one of these specified as noexcept and the other not?
To be honest, I'm not sure. It would seem that dereferencing a pointer should always be noexcept, however, unique_ptr allows you to completely change what the internal pointer type is (through the deleter). Now, as the user, you can define entirely different semantics for operator* on your pointer type. Maybe it computes things on the fly? All that fun stuff, which may throw.
Looking at std::shared_ptr we have this:
This is easy to explain - shared_ptr doesn't support the above-mentioned customization to the pointer type, which means the built-in semantics always apply - and *p where p is T* simply doesn't throw.
For what it's worth, here's a little of the history, and how things got the way they are now.
Before N3025, operator * wasn't specified with noexcept, but its description did contain a Throws: nothing. This requirement was removed in N3025:
Change [unique.ptr.single.observers] as indicated (834) [For details see the Remarks section]:
typename add_lvalue_reference<T>::type operator*() const;
1 - Requires: get() != 0nullptr.
2 - Returns: *get().
3 - Throws: nothing.
Here's the content of the "Remarks" section noted above:
During reviews of this paper it became controversial how to properly specify the operational semantics of operator*, operator[], and the heterogenous comparison functions. [structure.specifications]/3 doesn't clearly say whether a Returns element (in the absence of the new Equivalent to formula) specifies effects. Further-on it's unclear whether this would allow for such a return expression to exit via an exception, if additionally a Throws:-Nothing element is provided (would the implementor be required to catch those?). To resolve this conflict, any existing Throws element was removed for these operations, which is at least consistent with [unique.ptr.special] and other parts of the standard. The result of this is that we give now implicit support for potentially throwing comparison functions, but not for homogeneous == and !=, which might be a bit surprising.
The same paper also contains a recommendation for editing the definition of operator ->, but it reads as follows:
pointer operator->() const;
4 - Requires: get() != 0nullptr.
5 - Returns: get().
6 - Throws: nothing.
7 - Note: use typically requires that T be a complete type.
As far as the question itself goes: it comes down to a basic difference between the operator itself, and the expression in which the operator is used.
When you use operator*, the operator dereferences the pointer, which can throw.
When you use operator->, the operator itself just returns a pointer (which isn't allowed to throw). That pointer is then dereferenced in the expression that contained the ->. Any exception from dereferencing the pointer happens in the surrounding expression rather than in the operator itself.
Frankly, this just looks like a defect to me. Conceptually, a->b should always be equivalent to (*a).b, and this applies even if a is a smart pointer. But if *a isn't noexcept, then (*a).b isn't, and therefore a->b shouldn't be.
Regarding:
Is that saying that operator-> doesn't actually dereference anything?
No, the standard evaluation of -> for a type overloading operator-> is:
a->b; // (a.operator->())->b
I.e. the evaluation is defined recursively, when the source code contains a ->, operator-> is applied yielding another expression with an -> that can itself refer to a operator->...
Regarding the overall question, if the pointer is null, the behavior is undefined, and the lack of noexcept allows an implementation to throw. If the signature was noexcept then the implementation could not throw (a throw would be a call to std::terminate).