[#compliance] = Standard Compliance :idprefix: compliance_ :cpp: C++ == Closed-addressing Containers `unordered_[multi]set` and `unordered_[multi]map` are intended to provide a conformant implementation of the {cpp}20 standard that will work with {cpp}98 upwards. This wide compatibility does mean some compromises have to be made. With a compiler and library that fully support {cpp}11, the differences should be minor. === Move Emulation Support for move semantics is implemented using Boost.Move. If rvalue references are available it will use them, but if not it uses a close, but imperfect emulation. On such compilers: * Non-copyable objects can be stored in the containers. They can be constructed in place using `emplace`, or if they support Boost.Move, moved into place. * The containers themselves are not movable. * Argument forwarding is not perfect. === Use of Allocators {cpp}11 introduced a new allocator system. It's backwards compatible due to the lax requirements for allocators in the old standard, but might need some changes for allocators which worked with the old versions of the unordered containers. It uses a traits class, `allocator_traits` to handle the allocator adding extra functionality, and making some methods and types optional. During development a stable release of `allocator_traits` wasn't available so an internal partial implementation is always used in this version. Hopefully a future version will use the standard implementation where available. The member functions `construct`, `destroy` and `max_size` are now optional, if they're not available a fallback is used. A full implementation of `allocator_traits` requires sophisticated member function detection so that the fallback is used whenever the member function call is not well formed. This requires support for SFINAE expressions, which are available on GCC from version 4.4 and Clang. On other compilers, there's just a test to see if the allocator has a member, but no check that it can be called. So rather than using a fallback there will just be a compile error. `propagate_on_container_copy_assignment`, `propagate_on_container_move_assignment`, `propagate_on_container_swap` and `select_on_container_copy_construction` are also supported. Due to imperfect move emulation, some assignments might check `propagate_on_container_copy_assignment` on some compilers and `propagate_on_container_move_assignment` on others. === Construction/Destruction Using Allocators The following support is required for full use of {cpp}11 style construction/destruction: * Variadic templates. * Piecewise construction of `std::pair`. * Either `std::allocator_traits` or expression SFINAE. This is detected using Boost.Config. The macro `BOOST_UNORDERED_CXX11_CONSTRUCTION` will be set to 1 if it is found, or 0 otherwise. When this is the case `allocator_traits::construct` and `allocator_traits::destroy` will always be used, apart from when piecewise constructing a `std::pair` using `boost::tuple` (see <>), but that should be easily avoided. When support is not available `allocator_traits::construct` and `allocator_traits::destroy` are never called. === Pointer Traits `pointer_traits` aren't used. Instead, pointer types are obtained from rebound allocators, this can cause problems if the allocator can't be used with incomplete types. If `const_pointer` is not defined in the allocator, `boost::pointer_to_other::type` is used to obtain a const pointer. === Pairs Since the containers use `std::pair` they're limited to the version from the current standard library. But since {cpp}11 ``std::pair``'s `piecewise_construct` based constructor is very useful, `emplace` emulates it with a `piecewise_construct` in the `boost::unordered` namespace. So for example, the following will work: [source,c++] ---- boost::unordered_multimap x; x.emplace( boost::unordered::piecewise_construct, boost::make_tuple("key"), boost::make_tuple(1, 2)); ---- Older drafts of the standard also supported variadic constructors for `std::pair`, where the first argument would be used for the first part of the pair, and the remaining for the second part. === Miscellaneous When swapping, `Pred` and `Hash` are not currently swapped by calling `swap`, their copy constructors are used. As a consequence when swapping an exception may be thrown from their copy constructor. Variadic constructor arguments for `emplace` are only used when both rvalue references and variadic template parameters are available. Otherwise `emplace` can only take up to 10 constructors arguments. == Open-addressing Containers The C++ standard does not currently provide any open-addressing container specification to adhere to, so `boost::unordered_flat_set`/`unordered_node_set` and `boost::unordered_flat_map`/`unordered_node_map` take inspiration from `std::unordered_set` and `std::unordered_map`, respectively, and depart from their interface where convenient or as dictated by their internal data structure, which is radically different from that imposed by the standard (closed addressing). Open-addressing containers provided by Boost.Unordered only work with reasonably compliant C++11 (or later) compilers. Language-level features such as move semantics and variadic template parameters are then not emulated. The containers are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]. The main differences with C++ unordered associative containers are: * In general: ** `begin()` is not constant-time. ** `erase(iterator)` does not return an iterator to the following element, but a proxy object that converts to that iterator if requested; this avoids a potentially costly iterator increment operation when not needed. ** There is no API for bucket handling (except `bucket_count`). ** The maximum load factor of the container is managed internally and can't be set by the user. The maximum load, exposed through the public function `max_load`, may decrease on erasure under high-load conditions. * Flat containers (`boost::unordered_flat_set` and `boost::unordered_flat_map`): ** `value_type` must be move-constructible. ** Pointer stability is not kept under rehashing. ** There is no API for node extraction/insertion. == Concurrent Containers There is currently no specification in the C++ standard for this or any other concurrent data structure. `boost::concurrent_flat_map` takes the same template parameters as `std::unordered_map` and all the maps provided by Boost.Unordered, and its API is modelled after that of `boost::unordered_flat_map` with the crucial difference that iterators are not provided due to their inherent problems in concurrent scenarios (high contention, prone to deadlocking): so, `boost::concurrent_flat_map` is technically not a https://en.cppreference.com/w/cpp/named_req/Container[Container^], although it meets all the requirements of https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^] containers except those implying iterators. In a non-concurrent unordered container, iterators serve two main purposes: * Access to an element previously located via lookup. * Container traversal. In place of iterators, `boost::concurrent_flat_map` uses _internal visitation_ facilities as a thread-safe substitute. Classical operations returning an iterator to an element already existing in the container, like for instance: [source,c++] ---- iterator find(const key_type& k); std::pair insert(const value_type& obj); ---- are transformed to accept a _visitation function_ that is passed such element: [source,c++] ---- template size_t visit(const key_type& k, F f); template bool insert_or_visit(const value_type& obj, F f); ---- (In the second case `f` is only invoked if there's an equivalent element to `obj` in the table, not if insertion is successful). Container traversal is served by: [source,c++] ---- template size_t visit_all(F f); ---- of which there are parallelized versions in C++17 compilers with parallel algorithm support. In general, the interface of `boost::concurrent_flat_map` is derived from that of `boost::unordered_flat_map` by a fairly straightforward process of replacing iterators with visitation where applicable. If `iterator` and `const_iterator` provide mutable and const access to elements, respectively, here visitation is granted mutable or const access depending on the constness of the member function used (there are also `*cvisit` overloads for explicit const visitation). The one notable operation not provided is `operator[]`/`at`, which can be replaced, if in a more convoluted manner, by xref:#concurrent_flat_map_try_emplace_or_cvisit[`try_emplace_or_visit`]. //-