![]() |
Home | Libraries | People | FAQ | More |
This is the constraint on the Derived
template parameter to iterator_interface, view_interface
and sequence_container_interface:
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_interface 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.
struct
The interface templates rely mostly on public members provided by their Derived template parameter. However, iterator_interface
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.
iterator_interface
Can Act Like an Adaptor, And the Other Interface Templates Can't
There wouldn't be much point in adding this functionality to view_interface, because it only
uses the begin()
and end()
of the Derived type anyway.
For sequence_container_interface
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.
iterator_interface
Takes a Lot of Template Parameters, And the Other Interface Templates Don't
iterator_interface 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_iterator_interface 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_interface and sequence_container_interface
templates have very few template parameters. For view_interface, this is because
there are no member typedefs in the view
concept. For sequence_container_interface,
it was deemed ridiculous to create a template whose purpose is to reduce code
size, which takes 14 template parameters.
sequence_container_interface
Does not Deduce Nested Types Like iterator
sequence_container_interface
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
sequence_container_interface
may need to use some of these nested types — like iterator
— 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 sequence_container_interface.
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.
sequence_container_interface
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.
sequence_container_interface
Does not Satisfy the Allocator-Aware Container Requirements
It may not be immediately obvious, but sequence_container_interface
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. sequence_container_interface
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.