Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Rationale

There Are Minimal Derived-Type Contraints

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.

Using a Special Access-Granting 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.


PrevUpHomeNext