boost/libs/stl_interfaces/doc/rationale.qbk
2021-10-05 21:37:46 +02:00

97 lines
4.6 KiB
Plaintext

[/
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/]
[section Rationale]
[heading There Are Minimal Derived-Type Contraints]
This is the constraint on the `Derived` template parameter to _iter_iface_,
_view_iface_ and _cont_iface_:
std::enable_if_t<
std::is_class<Derived>::value &&
std::is_same<Derived, std::remove_cv_t<Derived>>::value>
This prevents instantiating an interface template with an `int`, a `const`
type, a reference type, etc.
Further constraints are not possible (for instance, that _view_iface_ is given
a `Derived` template parameter for a type that has a `begin()` and `end()`),
because `Derived` is an incomplete type within each *`_interface` template.
[heading Using a Special Access-Granting `struct`]
The interface templates rely mostly on public members provided by their
`Derived` template parameter. However, _iter_iface_ requires you to supply
`base_reference()` functions if you want it to act like an adaptor. Since at
least the non-`const` overload provides a non-`const` lvalue reference to one
of your types data members, it will break the encapsulation of many types to
leave `base_reference()` a public member. To allow users to keep these
overloads private, _access_ exists.
[heading _iter_iface_ Can Act Like an Adaptor, And the Other Interface Templates Can't]
There wouldn't be much point in adding this functionality to _view_iface_,
because it only uses the `begin()` and `end()` of the `Derived` type anyway.
For _cont_iface_ it also does not make much sense. Consider how many
container adaptors you've written. That's a use case that does not come up
often.
[heading _iter_iface_ Takes a Lot of Template Parameters, And the Other Interface Templates Don't]
_iter_iface_ does in fact take a lot of template parameters. However, it
usually only takes three: the `Derived` type, the iterator category, and the
iterator's `value_type`.
When you make a proxy iterator, you typically use the _proxy_iter_iface_
alias, and you again only need the same three template parameters. Though you
can opt into more template parameters, the rest are seldom necessary.
By contrast, the _view_iface_ and _cont_iface_ templates have very few
template parameters. For _view_iface_, this is because there are no member
typedefs in the `view` concept. For _cont_iface_, it was deemed ridiculous to
create a template whose purpose is to reduce code size, which takes 14
template parameters.
[heading _cont_iface_ Does not Deduce Nested Types Like `iterator`]
_cont_iface_ could deduce some of the nested types required for a standard
sequence container. For instance, `iterator` can be deduced as
`decltype(*begin())`. However, a type `D` derived from _cont_iface_ may need
to use some of these nested types _emdash_ like `iterator` _emdash_ in its
interface or implementation. If this is the case, those nested types are not
available early enough in the parse to be used in `D`, if they come from
deductions in _cont_iface_. This leaves the user in the awkward position of
defining the same nested type with a different name that can be used within
`D`. It seems better to leave these types for the user to define.
[heading _cont_iface_ Does not Support Associative or Unordered Associative Containers]
That's right. Associative containers have an interface that assumes that they
are node-based containers. On modern hardware, node-based containers are not
very efficient, and I don't want to encourage people to write more of them.
Unordered associative containers have an interface that precludes open
addressing. I don't want to encourage more of that either.
[heading _cont_iface_ Does not Satisfy the Allocator-Aware Container Requirements]
It may not be immediately obvious, but _cont_iface_ simply cannot help with
the allocator-aware requirements. All of the allocator-aware requirements but
3 are special members and constructors. A _CRTP_ base template is unable to
provide those, based on the language rules. That leaves the `allocator_type`
typedef, which the user must provide; member `swap()`, which is already a
container requirement (the allocator-aware table entry just specifies that
member `swap()` must be constant-time); and `get_allocator()`, which again is
something the user must provide.
Most of the difficulty of dealing with allocators has to do with the
implementation details of their use within your container. _cont_iface_
provides missing elements of a sequence container's interface, by calling
user-provided members of that same interface. It cannot help you with your
container's implementation.
[endsect]