Files
unordered/doc/unordered/compliance.adoc

146 lines
6.2 KiB
Plaintext
Raw Normal View History

[#compliance]
= Standard Compliance
2022-01-18 15:35:34 -08:00
:idprefix: compliance_
:cpp: C++
2022-10-30 19:16:43 +01:00
== Closed-addressing containers: unordered_[multi]set, unordered_[multi]map
2022-10-31 19:27:35 +01:00
The intent of Boost.Unordered is 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.
2022-01-18 15:35:34 -08:00
With a compiler and library that fully support {cpp}11, the differences should
be minor.
2022-10-30 19:16:43 +01:00
=== Move emulation
2022-01-18 15:35:34 -08:00
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.
2022-10-30 19:16:43 +01:00
=== Use of allocators
2022-01-18 15:35:34 -08:00
{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.
2022-10-30 19:16:43 +01:00
=== Construction/Destruction using allocators
2022-01-18 15:35:34 -08:00
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 <<compliance_pairs,below>>), but that should be easily avoided.
When support is not available `allocator_traits::construct` and
`allocator_traits::destroy` are never called.
2022-10-30 19:16:43 +01:00
=== Pointer Traits
2022-01-18 15:35:34 -08:00
`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<pointer, const value_type>::type`
is used to obtain a const pointer.
2022-10-30 19:16:43 +01:00
=== Pairs
2022-01-18 15:35:34 -08:00
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<std::string, std::complex> 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.
2022-10-30 19:16:43 +01:00
=== Miscellaneous
2022-01-18 15:35:34 -08:00
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.
2022-10-30 19:16:43 +01:00
== Open-addressing containers: unordered_flat_set/unordered_node_set, unordered_flat_map/unordered_node_map
2022-10-30 19:16:43 +01:00
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
2022-10-30 19:16:43 +01:00
`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).
2022-10-30 19:16:43 +01:00
Open-addressing containers provided by Boost.Unordered only work with reasonably
2022-10-30 19:16:43 +01:00
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^].
2022-10-30 19:16:43 +01:00
The main differences with C++ unordered associative containers are:
* In general:
** `begin()` is not constant-time.
** `erase(iterator)` returns `void` instead of an iterator to the following element.
** 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