97 lines
4.6 KiB
Plaintext
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]
|