std::set::find exception guarantees - c++

I'm currently writing exception-safe code and my design requires no throw guarantee for
set::find method.
I assume the comparator object always succeeds.
Does it imply set::find method will always succeed?
I thought about such a possibility after I have seen that according to http://en.cppreference.com/w/cpp/container/set/erase, set::erase method, with the same assumption, should always succeed and maybe there's a find in it (then it would be definetely worth a comment in documentation!)
The direct source for the problem is that I need to check whether an element is in a set and remove it from the set -- all that having no throw guarantee (it's in a catch block).

std::set::find:
Return value
Iterator to an element with key key. If no such element is found, past-the-end (see end()) iterator is returned.
Neither the documentation nor the C++ Standard explicitly list any exception safety guarantees. However, the same rules spelled out for std::set::erase apply here (§23.2.4.1 Exception safety guarantees [associative.reqmts.except]):
erase(k) does not throw an exception unless that exception is thrown by the container’s Compare object (if any).
In essence, unless the Compare object throws an exception, std::set::find does not throw. Bjarne Stroustrup has the following to say in The C++ Programming Language, Special Edition - Appendix E:
Fortunately, predicates rarely do anything that might throw an exception. However, user-defined <, ==, and != predicates must be taken into account when considering exception safety.
If you aren't providing any user-defined predicates you can assume std::set::find does not throw an exception. If you are, you should mark them as noexcept to work safely in your scenario.

C++11 §23.2.4.1 Exception safety guarantees [associative.reqmts.except] lists all the exception safety requirements for associative containers (including set) and there is no mention made of find. So no, the standard does not guarantee that find never throws.
In the absence of undefined behavior and assuming a non-throwing comparator, I find it extremely unlikely that an implementation of the C++ standard library exists which will throw an exception from set::find. I would personally be comfortable with (a) wrapping set::find in a noexcept forwarding function so that the program will crash if such an "impossible" thing ever occurs, or (b) wrapping a particular set::find call in
auto it = foo.end();
try {
it = foo.find(bar);
} catch(...) {}
and simply treating an exception as "not found."
Note that the ordering relation of associative containers is an easily-overlooked source of undefined behavior: § 23.2.4/2 requires the ordering relation to induce a strict weak ordering (as described in §25.4) on the key elements. An associative container instantiated with an ordering relation that is not a strict weak ordering does not have defined behavior, one possible outcome of which is throwing an exception from find.

Related

Is std::list's multi-element inserts strongly exception-safe?

In item 17 of exceptional c++, I find this:
First, for all containers, multi-element inserts ("iterator range"
inserts) are never strongly exception-safe.
but in item 1 of effective STL, I find this:
If you need transactional semantics for multiple-element insertions
(e.g., the range form — see Item 5), you'll want to choose list,
because list is the only standard container that offers transactional
semantics for multiple-element insertions.
and in page 249 of the c++ standard library 2th, I find this:
For lists, even multiple-element insert operations are transaction safe.
So my question is which one is right? Is strongly exceptional-safe means the same with transaction safe?
which one is right?
For all the overloads of std::list::insert, strongly exception-safety is guaranteed.
Exceptions
If an exception is thrown, there are no effects (strong exception guarantee).
and from the standard, $23.3.5.4/2 list modifiers [list.modifiers]:
If an exception is thrown there are no effects.
then
is strongly exceptional-safe means the same with transaction safe?
Yes. Here's an explanation from Herb Sutter:
Strong Guarantee: If an exception is thrown, program state remains unchanged. This level always implies global commit-or-rollback semantics, including that no references or iterators into a container be invalidated if an operation fails.
It is already answered that std::list provides this guarantees as per standard. I'd like to mention why it is possible to do this in the list.
You can provide this guarantee because list has a constant complexity merge operation, which is a non-throwing operation. All you need to do is to first create a temporary list, fill the temporary list with values and than merge temporary list into original list.
If exception happens while populating temporary list, nothing is merged, and temporary list is simply disposed when the insert exits.
Since no other container provides constant complexity no-throwing merge, it is not possible with any other container.

How can I find out the exact conditions when STL containers throw exceptions?

For STL containers (so far, std::vector<> and std::deque<>), I'm looking for documentation that says exactly when they throw exceptions. Something like, "It throws X in situation A. It throws Y in situation B. It throws no other exceptions under any circumstances."
I'd like to reassure my exception-phobic colleagues that we know exactly what can trigger exceptions in the STL classes we use.
The most accurate information will come from the C++ standard matching your compiler, and compiler documentation. However, the spec costs money. If you're willing to settle for a few typos, the draft C++11 specification can be found here: http://open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf for free, and the latest publicly available draft (preparing for C++14) seems to be http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf.
The number 1 used container is vector, so lets go over that.
Technically the only vector member that throws an exception is at, if given an out of range index. (tada! we're done!)
Less technically, vector assign/insert/emplace/reserve/resize/push_back/emplace_back/shrink_to_fit/etc can cause a resize, which uses std::allocator<T>:allocate, which can throw std::bad_alloc in theory. In weird situations with weird allocators, swap can also trigger this too. On some systems (Linux), this pretty much never happens, because it only throws if it runs out of virtual memory, and often the program will run out of physical memory first, and the OS will simply kill the whole program. That happens regardless of exceptions though, so this doesn't count against C++ exceptions.
Probably relevant is that the elements in a vector can throw any exception when copied, which affects constructors, assignment, insert/emplace/push_back/emplace_back, reserve, resize/shrink_to_fit. (If your element has a noexcept move constructor and move assignment, and it really really really should, then happens only when copying the entire vector).
The spec details exactly what exceptions are thrown and often also specifies under exactly what conditions they're thrown.
The C++ standard documents when exceptions will be thrown and under what circumstances for the standard library containers. There are also general rules about which methods will not throw exceptions for containers.
Alternatively, you can search the headers for throw (or the equivalent macro) to determine under what circumstances exceptions will trigger.

How do I find out which exceptions std::map methods can throw?

I would like to handle exceptional circumstances when using std::map (C++) methods, as well as when using things like boost::unordered_map methods. However, looking at method documentation (e.g.: insert) doesn't provide a list of exceptions that I can catch.
Look at good documentation: if you're not sure then - ultimately - the Standard, but e.g. http://en.cppreference.com/w/cpp/container/map/insert may be more convenient and has an Exceptions heading covering most variants (hopefully will be completed someday). You're not likely to find better documentation for boost than that kept online at the boost site.
If a good reference doesn't document exceptions, it's usually because that function's code isn't explicitly written to throw any - though that doesn't mean there can't be exceptions thrown as the arguments to a function are prepared, as a side effect of memory allocation or some reasonable operation on objects e.g. copy construction or operators, or during the construction of a result. All that's common sense though.
The Standard's explicit requirements re std::map...
23.2.4.1 Exception safety guarantees [associative.reqmts.except]
1 For associative containers, no clear() function throws an exception. erase(k) does not throw an exception
unless that exception is thrown by the container’s Compare object (if any).
2 For associative containers, if an exception is thrown by any operation from within an insert() function
inserting a single element, the insert() function has no effect.
3 For associative containers, no swap function throws an exception unless that exception is thrown by the
swap of the container’s Compare object (if any).
23.4.4.3 map element access [map.access]
T& at(const key_type& x);
const T& at(const key_type& x) const;
10 Throws: An exception object of type out_of_range if no such element is present.
Jorge,
Standard containers can only throw an out-of-range exception on access, they do not throw exceptions on other operations. However, the contained items can throw in an inner operation (constructor, assignation, comparison) when this operation is overloaded. That's why #tony-d link gives the best answer : this documents the behavior of the container when some underlying element raise during manipulations (insert/clear/swap).
The only exception left that can be raised are memory access violations and out-of-memory errors, which you should handle globally (and very carefully) at every high level, if at all.

Where can I find all the exception guarantees for the Standard Containers and Algorithms?

Yes, I've looked at the C++ standards that I could find (or the drafts), but I'm not finding any comprehensive of the exception guarantees given by STL containers. All I can find are occasional sections with incomplete descriptions on some of the functions for some of the types. Or perhaps it's there but I'm just not finding it, I don't know.
Note: I'm not asking for a list of all the guarantees people can think of, which is basically in this question.
I'm looking for the authoritative source of this information itself -- or preferably, a free version of the source (e.g. a draft of the standard) where I can more or less treat as official.
Reading the standard can be scary (let's come back to the standard), but Bjarne Stroustrup has written a really nice appendix on this subject in his book 'The C++ Programming Language'. He posted this appendix at
http://www.stroustrup.com/3rd_safe0.html , at
http://www.stroustrup.com/3rd_safe.pdf
It's pretty long and detailed (and well written). You may for example find section E.4 interesting, quote:
E.4 Standard Container Guarantees
If a library operation itself throws an exception, it can – and does –
make sure that the objects on which it operates are left in a
well-defined state. For example, at() throwing out_of_range for a
vector (§16.3.3) is not a problem with exception safety for the vector
. The writer of at() has no problem making sure that a vector is in a
well-defined state before throwing.
In addition, section E.4.1 states
In addition to the basic guarantee, the standard library offers the
strong guarantee for a few operations that insert or remove elements.
have a look at page 956. It contains a table of guarantees for various operations for vector, deque, list and map.
In summary, all operations on those containers are either nothrow or strong, except for N - element insert into map which offers the basic guarantees.
Note: the above text is old and does not address C++11, but should still be correct enough for most aims and purposes.
When it comes to C++11...
the standard first states, about the containers
array, deque, forward_list, list, vector, map, set, unordered_map, unordered_set, queue,stack:
at
23.2.1/10:
Unless otherwise specified (see 23.2.4.1, 23.2.5.1, 23.3.3.4, and
23.3.6.5) all container types defined in this Clause meet the following additional requirements:
— if an exception is thrown by an insert() or emplace() function while
inserting a single element, that function has no effects.
— if an exception is thrown by a push_back() or push_front() function,
that function has no effects.
— no erase(), clear(), pop_back() or pop_front() function throws an
exception.
— no copy constructor or assignment operator of a returned iterator
throws an exception.
— no swap() function throws an exception.
— no swap() function invalidates any references, pointers, or
iterators referring to the elements of the containers being swapped.
The quirks pointed out in the respective sections referred to above (each called Exception safety guarantees) are mostly about special against-the-wall cases like when dealing with exceptions from the contained types' hashing, comparison operations as well as throwing swap and throwing move operations.
n3376
23.2.1 General container requirements [container.requirements.general]
Paragraph 10
Unless otherwise specified (see 23.2.4.1, 23.2.5.1, 23.3.3.4, and 23.3.6.5) all container types defined in this Clause meet the following additional requirements:
— if an exception is thrown by an insert() or emplace() function while inserting a single element, that function has no effects.
— if an exception is thrown by a push_back() or push_front() function, that function has no effects.
— no erase(), clear(), pop_back() or pop_front() function throws an exception.
— no copy constructor or assignment operator of a returned iterator throws an exception.
— no swap() function throws an exception.
— no swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped.
[Note: The end() iterator does not refer to any element, so it may be invalidated. —endnote]
23.2.4 Associative containers [associative.reqmts]
23.2.4.1 Exception safety guarantees [associative.reqmts.except]
1 For associative containers, no clear() function throws an exception. erase(k) does not throw an exception unless that exception is thrown by the container’s Compare object (if any).
2 For associative containers, if an exception is thrown by any operation from within an insert or emplace function inserting a single element, the insertion has no effect.
3 For associative containers, no swap function throws an exception unless that exception is thrown by the swap of the container’s Compare object (if any).
23.2.5 Unordered associative containers [unord.req]
23.2.5.1 Exception safety guarantees [unord.req.except]
1 For unordered associative containers, no clear() function throws an exception. erase(k) does not throw an exception unless that exception is thrown by the container’s Hash or Pred object (if any).
2 For unordered associative containers, if an exception is thrown by any operation other than the container’s hash function from within an insert or emplace function inserting a single element, the insertion has no effect.
3 For unordered associative containers, no swap function throws an exception unless that exception is thrown by the swap of the container’s Hash or Pred object (if any).
4 For unordered associative containers, if an exception is thrown from within a rehash() function other than by the container’s hash function or comparison function, the rehash() function has no effect.
23.3.3.4 deque modifiers [deque.modifiers]
void push_back(T&& x); Paragraph 2
Remarks: If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T there are no effects. If an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.
iterator erase(const_iterator first, const_iterator last); Paragraph 6
Throws: Nothing unless an exception is thrown by the copy constructor, move constructor, assignment operator, or move assignment operator of T.
23.3.6.5 vector modifiers [vector.modifiers]
void push_back(T&& x); Paragraph 2
If an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.
iterator erase(const_iterator first, const_iterator last); Paragraph 5
Throws: Nothing unless an exception is thrown by the copy constructor, move constructor, assignment operator, or move assignment operator of T.
The document you've linked to, the n3337 draft standard, can be treated as official. It's the C++11 standard plus minor editorial changes.
You just need to learn to read the standard, which is understandable because it's not intended to be easy reading.
To find the exception guarantees for any particular library operation, check that operation's specification for remarks and comments on exceptions. If the function is a member function then check the specification of the type for comments on exception safety and what requirements it fulfills. Then check the fulfilled requirements for exception guarantees that must be made by objects to fulfill those requirements.
For generic types and algorithms also check the requirements placed on the template parameters in order to see what requirements those types have to meet in order for all the exception guarantees made by the type or algorithm or member function to hold (if the template parameters don't meet the specified requirements then using the template with those parameters has undefined behavior and none of the template's specifications apply).

Can any C++ 98 standard container operations throw std::bad_alloc?

I was trying to work out if the try catch is needed in the following code:
std::vector<int> values;
// ignore that this can throw std::bad_alloc
values.push_back(1);
try {
for (std::vector<int>::iterator iter = values.begin();
iter != values.end(); ++iter) {
++(*iter);
}
} catch (const std::bad_alloc&) {
// Is this needed?
}
Looking through the C++ 1998 standard the only thing I can find which hints at this is section 23.1 "Container requirements" bullet point 8 which contains the sentence:
A copy of this argument is used for any memory allocation performed, by these constructors and by all member functions, during the lifetime of each container object.
My interpretation of this is that any member function in a container can call the allocator so any member function can throw std::bad_alloc. Am I being overly paranoid or is this really the case?
If you carry on reading a bit further, you'll find 23.1/10, which gives the containers' requirements regarding when exceptions can be thrown, in particular:
no erase(), pop_back() or pop_front() function throws an exception.
no copy constructor or assignment operator of a returned iterator throws an exception.
If you're truly paranoid, then you should consider the possiblity of begin() and end(), and even iterator increment, throwing; but there's no need for them to do anything complicated in any sane implementation of the standard containers.
Theorotically Yes, any standard library container member function can throw bad_alloc.
Most standard Containers themselves do not throw any exceptions except (std::vector::at()) but they can throw exceptions for memory allocation failures or exceptions of user-defined operations.
I assume your fear is that push_back() throws in your example case, if not then yes you are being paranoid. Eventhough, it is an implementation detail In practice almost none of the implemenatations will try to allocate if you are just getting iterators(begin(), end()).
I didn't find something more specific in the standard, so in the absence of evidence we 'll have to conclude that technically any container method can throw bad_alloc.
I wouldn't worry about begin and end throwing as far as the container itself is concerned, but there's another troubling point: iterator implementation. If iterators are of class type then theoretically the construction (or assignment) of such instances could very well throw. So while I don't think it's probable that any randomly chosen standard library implementation would throw, you can't really rule it out.