Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there no `noexcept` specifier on std::unordered_map::count?

I was reading C++ reference page about std::unordered_map. The empty and size methods are noexcept qualified, but not count.

I don't think it should throw in count.

Am I missing something?

like image 246
Richard Dally Avatar asked Feb 16 '16 22:02

Richard Dally


1 Answers

Because the requirements say so:

count returns the number of elements matching a specific key, and key comparisons are evaluated off of an object of type X::key_type for any unordered associative container type X (instantiated std::unordered_maps are such containers)

n3337 23.2.5/5 [unord.req]

Two values k1 and k2 of type Key are considered equivalent if the container’s key equality predicate returns true when passed those values. ... For any two keys k1 and k2 in the same container, callingpred(k1, k2) shall always return the same value. ...

For an unordered map, X::key_type is defined as part of its template parameter list:

template<
    class Key,
    //    ^^^ member type key_type set from this parameter
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    //    ^^^^^^^^ member type key_equal set from this parameter
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;

The only constraints I can find on key_type also apply to value_type:

n3337 23.2.5/9 [unord.req]2

... the requirements placed on value_type in Table 96 apply instead to key_type and mapped_type.

So we just need to know the requirements placed on value_type in table 96, which specifies the requirements for a Container. In the very first row, we have:

n3337, table 963

X::value_type | Returns T | Requires: T is Destructible

where X is again the type of the Container, and T is the type of objects it's storing. Destructibleobjects are not allowed to have throwing destructors. That's their only requirement.

n3337, table 24

u.∼T() All resources owned by u are reclaimed, no exception is propagated

(u being an object of type T which fulfills the requirements of Destructible)

Thus, there is no restriction on the throw guarantees offered by the key comparison function for the unordered_map, and so no guarantee on the operator== operation provided from std::equal_to to implement the required behavior. The key itself lacks any such restrictions, so: the comparison function is allowed to throw, and any function which uses the comparison function is also allowed to throw. count needs to count the stored values with keys that match the supplied key with the comparison function, so it may throw.


clear may be noexcept because the standard bans throwing in destructors:

17.6.4.8/1,24 [res.on.functions]

In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.

In particular, the effects are undefined in the following cases:

...

  • if any replacement function or handler function or destructor operation exits via an exception, unless specifically allowed in the applicable Required behavior: paragraph.

...

since the only client-dependent code clear executes may not throw exceptions, and the implementation doesn't need to, it may be and has been marked noexcept


Notes:

1. The n4140 standard draft (near c++14) does not appear to have changed this clause at all.

2. n4140 retains this phrasing, moved to clause 10 from clause 9.

3. The Container requirements are also listed in Table 96 of n4140, and list the requirement for T as being Erasable, which also places no restrictions on operator==

4. The wording of this clause hasn't changed in n4140.

like image 130
jaggedSpire Avatar answered Sep 20 '22 06:09

jaggedSpire