File: rationale.qbk

package info (click to toggle)
boost1.74 1.74.0-9
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 464,084 kB
  • sloc: cpp: 3,338,324; xml: 131,293; python: 33,088; ansic: 14,336; asm: 4,034; sh: 3,351; makefile: 1,193; perl: 1,036; yacc: 478; php: 212; ruby: 102; lisp: 24; sql: 13; csh: 6
file content (96 lines) | stat: -rw-r--r-- 4,740 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
[/
 / 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]