Document number: P0458R2 Date: 2018-06-06 Project: LWG Reply-to: Mikhail Maltsev <maltsevm@gmail.com>

Checking for Existence of an Element in Associative Containers

This paper proposes to add a member function contains , which checks, whether or not a given element exists in a container, to standard associative containers.

Two missing const qualifiers were added

Rebased to the latest standard draft

Added a separate table row for the transparent lookup

The common idiom for checking whether an element exists in an associative container involves doing a lookup and checking the returned iterator:

if (some_set.find(element) != some_set.end()) { // ... }

This idiom suffers from excessive boilerplate code and is inferior to if (some_set.contains(element)) in terms of expressing intent in code.

It is also not obvious for beginners. Quoting a question from Stack Overflow [1]:

How to check if std::map contains a key without doing insert? The only way I have found to check for duplicates is by inserting and checking the std::pair.second for false, but the problem is that this still inserts something if the key is unused, whereas what I want is a map.contains(key); function.

Another (a little less common) way to perform the same task is using the count method. Unfortunately, its use is suboptimal for multisets and multimaps (it has greater complexity than find ). In some sense it is similar to the size method of the standard library containers: it does not replace the empty method.

The idea of this proposal is not new. It was discussed on "std-proposals" forum [2], but unfortunately that discussion did not result in a formal proposal.

Qt library includes QSet and QMap class templates, similar to the standard ones, but they also provide contains member function [3].

As Thiago Macieira (one of Qt developers) pointed out in [2], in Qt itself, contains method is used much more widely than find .

Arguably, for sets ( std::set and std::unordered_set ) testing a key for existence is much more common than using the find method for any other purpose, yet it has less obvious API.

contains could as well be added as a free function. One might even argue, that decoupling of algorithms and containers is one of the core principles of the standard library. Nevertheless, lookup by key is inherent to associative containers, i.e. find member function is not equivalent to std::find . So, if we were to add std::contains , it should work on ranges and be equivalent to std::find(first, last, element) != last for consistency.

Another (non-)option would be to make iterators returned by associative containers contextually convertible to bool, i.e. make if (some_set.find(element))

equivalent to

if (some_set.find(element) != some_set.end())

In some implementations adding such a conversion would be possible without breaking the ABI. For example, this would definitely be possible for unordered associative containers in GCC (libstdc++'s unordered_set::iterator consists of a single pointer and for unordered_set::end this pointer is nullptr ). Unfortunately this might not be the case in general, so this option is not proposed

In [associative.reqmts], "Table 84 — Associative container requirements (in addition to container)", add:

Expression Return type Assertion/note pre-/post-condition Complexity b.contains(k) bool equivalent to b.find(k) != b.end() logarithmic a_tran.contains(ke) bool equivalent to a_tran.find(ke) != a_tran.end() logarithmic

In [unord.req], "Table 85 — Unordered associative container requirements (in addition to container)", add:

Expression Return type Assertion/note pre-/post-condition Complexity b.contains(k) bool equivalent to b.find(k) != b.end() Average case O(1), worst case O(b.size())

In [map.overview], in section "map operations" add:

bool contains(const key_type& x) const; template <class K> bool contains(const K& x) const;

In [multimap.overview], in section "map operations" add:

bool contains(const key_type& x) const; template <class K> bool contains(const K& x) const;

In [set.overview], in section "set operations" add:

bool contains(const key_type& x) const; template <class K> bool contains(const K& x) const;

In [multiset.overview], in section "set operations" add:

bool contains(const key_type& x) const; template <class K> bool contains(const K& x) const;

In [unord.map.overview], in section "map operations" add:

bool contains(const key_type& k) const;

In [unord.multimap.overview], in section "map operations" add:

bool contains(const key_type& k) const;

In [unord.set.overview], in section "set operations" add:

bool contains(const key_type& k) const;

In [unord.multiset.overview], in section "set operations" add:

bool contains(const key_type& k) const;