1[/ 2 / Distributed under the Boost Software License, Version 1.0. (See accompanying 3 / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 /] 5 6[section Tutorial: `iterator_interface`] 7 8[note All the member functions provided by _iter_iface_ are in your iterator's 9base class _emdash_ _iter_iface_ _emdash_ and can therefore be hidden if you 10define a member function with the same name in your derived iterator. If you 11don't like the semantics of any _iter_iface_-provided member function, feel 12free to replace it.] 13 14[heading The `iterator_interface` Template] 15 16Though a given iterator may have a large number of operations associated with 17it, there are only a few basis operations that the iterator needs to define; 18the full set of operations it supports can be defined in terms of that much 19smaller basis. 20 21It is possible to define any iterator `Iter` in terms of a subset of 22user-defined operations. By deriving `Iter` from _iter_iface_ using _CRTP_, 23we can generate the full set of operations. Here is the declaration of 24_iter_iface_: 25 26 template< 27 typename Derived, 28 typename IteratorConcept, 29 typename ValueType, 30 typename Reference = ValueType &, 31 typename Pointer = ValueType *, 32 typename DifferenceType = std::ptrdiff_t> 33 struct iterator_interface; 34 35Let's break that down. 36 37`Derived` is the type that you're deriving _iter_iface_ from. 38 39`IteratorConcept` defines the iterator category/concept. This must be one of 40the C++ standard iterator tag types, like `std::forward_iterator_tag`. In 41C++20 and later, `std::contiguous_iterator_tag` is a valid tag to use. 42 43`ValueType` is used to define the iterator's `value_type` typedef. Likewise, 44`Reference` and `Pointer` are used to define the iterator's `reference` and 45`pointer` typedefs, respectively. 46 47[tip `Reference` does not need to be a reference type, and `Pointer` does not 48need to be a pointer type. This fact is very useful when making proxy 49iterators. ] 50 51`DifferenceType` is used to define the iterator's `difference_type`. Don't be 52a weirdo; just leave this as the default type, `std::ptrdiff_t`. 53 54[heading Proxy Iterators] 55 56Sometimes you need to create an iterator `I` such that `I::reference_type` is 57not a (possibly `const`) reference to `I::value_type`. For instance, let's 58say you want to make a zip-iterator that produces pairs of values from two 59separate underlying sequences. For sequences `A` and `B`, with respective 60`value_type`s `T` and `U`, one possible `reference_type` for a zip iterator 61would be `std::pair<T &, U &>` (this is distinct from a reference to a pair, 62such as `std::pair<T, U> &`). Each such pair would contain a reference to one 63element from `A` and a reference to the corresponding element from `B`. 64 65As another example, if you wanted an iterator `I` that represents the infinite 66sequence 0, 1, 2, ..., you'd be unable to form a reference to most or all of 67those values; you'd instead produce a temporary for each value as it is 68needed. This implies that `I::value_type` does not involve references at all; 69it may instead by `int` or `double`. 70 71When defining a proxy iterator, you can use a template alias that provides 72reasonable defaults for _iter_iface_'s parameters: 73 74 template< 75 typename Derived, 76 typename IteratorConcept, 77 typename ValueType, 78 typename Reference = ValueType, 79 typename DifferenceType = std::ptrdiff_t> 80 using proxy_iterator_interface = iterator_interface< 81 Derived, 82 IteratorConcept, 83 ValueType, 84 Reference, 85 proxy_arrow_result<Reference>, 86 DifferenceType>; 87 88[note As shown above, _proxy_iter_iface_ uses a template called 89`proxy_arrow_result` as its pointer-type. This template makes a copy of 90whatever value is obtained by `operator*`, and then returns a pointer to the 91copy in its `operator->`. You may want to use something else if this is a 92performance concern. ] 93 94 95[heading User-Defined Iterator Operations] 96 97Now, let's get back to the user-defined basis operations. 98 99In the table below, `Iter` is a user-defined type derived from _iter_iface_; 100`i` and `i2` are objects of type `Iter`; `reference` is the type passed as the 101`Reference` template parameter to _iter_iface_; `pointer` is the type passed 102as the `Pointer` template parameter to _iter_iface_; and `n` is a value of 103type `difference_type`. 104 105[table User-Defined Operations 106 [[Expression] [Return Type] [Semantics] [Assertion/note/pre-/post-condition]] 107 [ 108 [ `*i` ] 109 [ Convertible to `reference`. ] 110 [ Dereferences `i` and returns the result. ] 111 [ ['Expects:] i is dereferenceable. ] 112 ] 113 [ 114 [ `i == i2` ] 115 [ Contextually convertible to `bool`. ] 116 [ Returns true if and only if `i` and `i2` refer to the same value. ] 117 [ ['Expects:] `(i, i2)` is in the domain of `==`. ] 118 ] 119 [ 120 [ `i2 - i` ] 121 [ Convertible to `difference_type`. ] 122 [ Returns `n`. ] 123 [ ['Expects:] there exists a value `n` of type `difference_type` such that `i + n == i2`. 124 `i2 == i + (i2 - i)`. ] 125 ] 126 [ 127 [ `++i` ] 128 [ `Iter &` ] 129 [ Increments `i`. ] 130 [ ] 131 ] 132 [ 133 [ `--i` ] 134 [ `Iter &` ] 135 [ Decrements `i`. ] 136 [ ] 137 ] 138 [ 139 [ `i += n` ] 140 [ `Iter &` ] 141 [ 142``difference_type m = n; 143if (m >= 0) 144 while (m--) ++i; 145else 146 while (m++) --i;`` ] 147 [ ] 148 ] 149] 150 151[tip The table above leaves a lot of implementation freedom. In 152`operator+=()`, you could take `n` as a value or as a reference; `operator-()` 153can return a `difference_type` or just something convertible to one; etc. In 154particular, your operations can be `constexpr` or `noexcept` as you see fit.] 155 156Not all the iterator concepts require all the operations above. Here are the 157operations used with each iterator concept: 158 159[table Operations Required for Each Concept 160 [[Concept] [Operations]] 161 [ 162 [ `input_iterator` ] 163 [ ``*i 164i == i2 165++i`` ] 166 ] 167 [ 168 [ `output_iterator` ] 169 [ ``*i 170++i`` ] 171 ] 172 [ 173 [ `forward_iterator` ] 174 [ ``*i 175i == i2 176++i`` ] 177 ] 178 [ 179 [ `bidirectional_iterator` ] 180 [ ``*i 181i == i2 182++i 183--i`` ] 184 ] 185 [ 186 [ `random_access_iterator`/`continguous_iterator` ] 187 [ ``*i 188i - i2 189i += n`` ] 190 ] 191] 192 193[note For `random_access_iterator`s, the operation `i - i2` is used to provide 194all the relational operators, including `operator==()` and `operator!=()`. If 195you are defining an iterator over a discontiguous sequence 196(e.g. `std::deque`), this implementation of `operator==()` may not be optimal. 197In this case, provide your own `operator==()`. `operator!=()` will be 198provided if `operator==` is available. ] 199 200[heading An Important Note About `operator++()` and `operator--()`] 201 202There's a wrinkle in this way of doing things. When you define `operator++()` 203in your iterator type `Derived`, _iter_iface_ defines post-increment, 204`operator++(int)`. But since `Derived` has an `operator++` and so does its 205base class _iter_iface_, the one in `Derived` *hides* the one in _iter_iface_. 206 207So, you need to add a using declaration that makes the `operator++` from the 208base class visible in the derived class. For instance, in the `node_iterator` 209example there are these lines: 210 211[node_iterator_using_declaration] 212 213[important All of the above applies to `operator--`. So, for bidirectional 214iterators, you need to add a line like `using base_type::operator--;` as 215well. ] 216 217[note These using declarations are not necessary for a random access iterator, 218because `Derived` does not have an `operator++()` in that case. ] 219 220[heading Putting it All Together] 221 222Ok, let's actually define a simple iterator. Let's say you need to interact 223with some legacy code that has a hand-written linked list: 224 225[node_defn] 226 227We can't change this code to use `std::list`, but it would be nice to be able 228to reuse all of the standard algorithms with this type. Defining an iterator 229will get us there. 230 231[node_iterator_class_head] 232 233We are deriving `node_iterator` from _iter_iface_, and because we're using 234_CRTP_, we first have to pass `node_iterator` for the `Derived` template 235parameter, so that _iter_iface_ knows what derived type to cast to in order to 236get at the user-defined operations. Then, we pass `std::forward_iterator_tag` 237for `IteratorConcept`, since that's appropriate for a singly-linked list. 238Finally, we pass `T` to let _iter_iface_ know what the `value_type` is for our 239iterator. 240 241We leave the rest of the template parameters at their defaults: `T &` for 242`Reference`, `T *` for `Pointer`, and `std::ptrdiff_t` for `DifferenceType`. 243This is what you will do for almost all iterators. The most common exceptions 244to this are usually some kind of proxy iterator. Another exception is when 245for better code generation you want to return builtin values instead of 246references for constant iterators. To see an example of the latter, see the 247`repeated_chars_iterator` in the introduction; it's `Reference` template 248parameter is `char` for this reason. 249 250[node_iterator_ctors] 251 252Next, we define two constructors: a default constructor, and one that takes a 253`node` pointer. A default constructor is required by the `forward_iterator` 254concept, but _iter_iface_ cannot supply this, since constructors are not 255visible in derived types without user intervention. 256 257[important A default constructor is required for every iterator concept.] 258 259[node_iterator_user_ops] 260 261Next, we define the user-defined operations that _iter_iface_ requires to do 262its work. As you might expect, the three required operations are very 263straightforward. 264 265[note Here, I implement `operator==()` as a hidden friend function. it would 266have worked just as well if I had instead implemented it as a member function, 267like this: 268 269``constexpr bool operator==(node_iterator rhs) const noexcept 270{ 271 return it_ == rhs.it_; 272}`` 273 274Either of these forms works, since _iter_iface_ is concept-based _emdash_ the 275appropriate expressions need to be well-formed for the _iter_iface_ tempalte 276to do its work. ] 277 278Finally, we need a using declaration to make 279`iterator_interface::operator++(int)` visible: 280 281[node_iterator_using_declaration] 282 283Here's how we might use the forward iterator we just defined: 284 285[node_iterator_usage] 286 287[heading What About Adapting an Existing Iterator?] 288 289So glad you asked. If you want to make something like a filtering iterator, 290or say a UTF-8 to UTF-32 transcoding iterator, you are starting with an 291existing iterator and adapting it. There's a way to avoid having to write all 292of the user-defined basis functions, as long as there's a base iterator that 293already has the right operations with the right semantics. 294 295For example, consider an iterator that contains a pointer to an array of 296`int`, and predicate of type `Pred`. It filters out integers that do not meet 297the predicate. Since we are using an existing iterator (the pointer to 298`int`), we already have all the operations we need for a bidirectional 299iterator (and more), except that `operator++` on an `int *` does not skip over 300elements as we'd like. Here's the code: 301 302[filtered_int_iterator_defn] 303 304So, all we had to do was let _iter_iface_ know that there was an underlying 305iterator it could use _emdash_ by implementing `base_reference()` _emdash_ and 306the operations that we did not define got defined for us by _iter_iface_. 307 308Here is the iterator in action: 309 310[filtered_int_iterator_usage] 311 312[heading Checking Your Work] 313 314_IFaces_ is able to check that some of the code that you write is compatible 315with the concept for the iterator you're writing. It cannot check everything. 316For instance, _IFaces_ does not know if your derived type includes a default 317constructor, which is required by all the iterators. In particular, 318_iter_iface_ cannot `static_assert` on the wellformedness of `Derived()`, 319since `Derived` is an incomplete type within the body of _iter_iface_ 320_emdash_ _iter_iface_ is the base class for `Derived`, not the other way 321round. 322 323Since you can easily `static_assert` that a type models a given concept, a 324good practice is to put such a `static_assert` after you define your iterator 325type. 326 327For instance, after `node_iterator` you'll find this code: 328 329[node_iterator_concept_check] 330 331Consider this good code hygiene. Without this simple check, you'll probably 332eventually find yourself looking at an error message with a very long template 333instantiation stack. 334 335There's also a macro that can help you check that `std::iterator_traits` is 336well-formed and provides the correct types. See _traits_m_. 337 338[endsect] 339 340[section Tutorial: `view_interface`] 341 342[note All the member functions provided by _view_iface_ are in your view's 343base class _emdash_ _view_iface_ _emdash_ and can therefore be hidden if you 344define a member function with the same name in your derived view. If you 345don't like the semantics of any _view_iface_-provided member function, feel 346free to replace it.] 347 348[heading The `view_interface` Template] 349 350C++20 contains a _CRTP_ template, `std::ranges::view_interface`, which takes a 351range or view, and adds all the operations that view types have, using only 352the range's/view's `begin()` and `end()`. This is a C++14-compatible version 353of that template. 354 355As with _iter_iface_, _view_iface_ makes it possible to write very few 356operations _emdash_ only `begin()` and `end()` are actually used by 357_view_iface_ _emdash_ and get all the other operations that go with view 358types. The operations added depend on what kinds of iterator and/or sentinel 359types your derived view type defines. 360 361Here is the declaration of _view_iface_: 362 363 template<typename Derived, element_layout Contiguity = element_layout::discontiguous> 364 struct view_interface; 365 366_view_iface_ only requires the derived type and an optional non-type template 367parameter that indicates whether `Derived`'s iterators are contiguous. The 368non-type parameter is necessary to support pre-C++20 code. 369 370[note Proxy iterators are inherently discontiguous.] 371 372In this example, we're implementing something very similar to 373`std::ranges::drop_while_view`. First, we need helper view types `subrange` 374and `all_view`, and a function that takes a range and returns a view of the 375entire range, `all()`: 376 377[all_view] 378 379Note that `subrange` is derived from _view_iface_, so it will have all the 380view-like operations that are appropriate to its `Iterator` and `Sentinel` 381types. 382 383With the helpers available, we can define `drop_while_view`: 384 385[drop_while_view_template] 386 387Now, let's look at code using these types, including operations defined by 388_view_iface_ that we did not have to write: 389 390[drop_while_view_usage] 391 392If you want more details on _view_iface_, you can find it wherever you usually 393find reference documentation on the standard library. We won't cover it here 394for that reason. See [@http://eel.is/c++draft/view.interface [view.interface] 395on eel.is] or [@https://cppreference.com] for details. 396 397[endsect] 398 399[section Tutorial: `sequence_container_interface`] 400 401[note All the member functions provided by _cont_iface_ are in your 402container's base class _emdash_ _cont_iface_ _emdash_ and can therefore be 403hidden if you define a member function with the same name in your derived 404container. If you don't like the semantics of any _cont_iface_-provided 405member function, feel free to replace it.] 406 407[heading The `sequence_container_interface` Template] 408 409As mentioned earlier, writing containers is very tedious. The container 410requirements tables in the C++ standard are long and complicated, and there 411are a lot of them. The requirements often call for multiple overloads of a 412function, all of which could be implemented in terms of just one overload. 413 414There is a large development cost associated with implementing a 415standard-compliant container. As a result very few people do so. 416_cont_iface_ exists to make bring that large development time way, way down. 417 418Here is its declaration: 419 420 template<typename Derived, element_layout Contiguity = element_layout::discontiguous> 421 struct sequence_container_interface; 422 423Just as with _view_iface_, _cont_iface_ takes the derived type and an optional 424non-type template parameter that indicates whether `Derived`'s iterators are 425contiguous. The non-type parameter is necessary to support pre-C++20 code. 426 427[heading How `sequence_container_interface` is Organized] 428 429The tables below represent a subset of the operations needed for each of the 430container requirements tables in the standard. Here are the tables that apply 431to sequence containers (from `[container.requirements]` in the standard): 432 433* Container requirements `[tab:container.req]` 434* Reversible container requirements `[tab:container.rev.req]` 435* Optional container operations `[tab:container.opt]` 436* Allocator-aware container requirements `[tab:container.alloc.req]` 437* Sequence container requirements `[tab:container.seq.req]` 438* Optional sequence container operations `[tab:container.seq.opt]` 439 440Each requirements table lists all the types and operations required by a 441standard-conforming container. All of these sets of requirements are 442supported by _cont_iface_, except the allocator-aware container requirements. 443The container and sequence container requirements are required for any 444sequence container. _cont_iface_ provides each member in any table above 445(again, except the allocator-aware ones). Each member is individually 446constrained, so if a given member (say, a particular `insert()` overload) is 447ill-formed, it will not be usable in the resulting container. 448 449[note All table requirements satisfied by _IFaces_ use the 2017 version of the 450C++ Standard. See your favorite online resource for the contents of these 451tables. Many people like [@http://eel.is/c++draft eel.is], which is really 452easy to navigate.] 453 454Note that _cont_iface_ does not interact at all with the allocator-aware 455container requirements, the associative container requirements, or the 456unordered associative container requirements. Specifically, nothing precludes 457you from satisfying any of those sets or requirements _emdash_ it's just that 458_cont_iface_ does not. 459 460[heading How `sequence_container_interface` Works] 461 462To use _cont_iface_, you provide certain operations yourself, and _cont_iface_ 463fills in the rest. If any provided operation `O` is not to your liking 464_emdash_ say, because you know of a way to do `O` directly in a way that is 465more efficient than the way that _cont_iface_ does it _emdash_ you can 466implement `O` yourself. Since your implementation is in a class `D` derived 467from _cont_iface_, it will hide the `O` from _cont_iface_. 468 469Below, there are tables that show what user-defined types and operations are 470required for _cont_iface_ to fulfill all the requirements from one of the C++ 471Standard's requirements tables. For instance, the table "Optional 472User-Defined Types and Operations for Containers" below shows what you need to 473provide to fulfill all the requirements in the standard's "Container 474requirements `[tab:container.req]`" table. 475 476So, to use _cont_iface_ to make a `std::array`-like container (which is not a 477sequence container, because it has no `insert()`, `erase()`, etc.), you need 478to define the types and operations in the "User-Defined Types and Operations 479for Containers" table, and optionally the ones in the "Optional User-Defined 480Types and Operations for Containers". 481 482To make a `std::forward_list`-like type, you need to define the types and 483operations in the "User-Defined Types and Operations for Containers" table, 484and optionally the ones in the "Optional User-Defined Types and Operations for 485Containers". You would also define the types and operations in the 486"User-Defined Types and Operations for Sequence Containers" table. You cannot 487define the types and operations in the "User-Defined Types and Operations for 488Reversible Containers" table, because your container is forward-only. 489 490To make a `std::vector`-like type, you would provide the types and operations 491in all the tables below. 492 493If you have a type that does not have all the operations in one of the tables, 494that's fine -- you can just implement the operations that your type can do, 495and whatever operations can be provided by _cont_iface_ in terms of the 496user-defined operations, will be provided. For example, the `std::array`-like 497container described above would have `front()` _emdash_ which comes from the 498optional sequence container requirements _emdash_ even if you did not write 499any user-defined insertion or erasure member functions into your container. 500If it has bidirectional iterators, the `std::array`-like container will have 501`back()` too. 502 503[heading The `sequence_container_interface` Tables] 504 505After each requirements table, there's a table indicating how _cont_iface_ 506maps the user-defined operations to the operations it provides. These mapping 507tables can be handy if you have a container that meets only some of the 508requirements of one of the requirements tables. 509 510In the tables, `X` is a user-defined type derived from _cont_iface_ containing 511objects of type `T`; `a` and `b` are objects of type `X`; `i` and `j` are 512objects of type (possibly const) `X::iterator`; `u` is an identifier; `r` is a 513non-const value of type `X`; `rv_c` is a non-const rvalue of type `X`; `i` and 514`j` are forward iterators that refer to elements implicitly convertible to 515`T`; `[i, j)` is a range; `il` is an object of type 516`std::initializer_list<T>`; `n` is a value of type `X::size_type`, `p` is a 517valid constant iterator to `a`; `q` is a valid dereferenceable constant 518iterator to `a`; `[q1, q2)` is a valid range of constant iterators to `a`; `t` 519is an lvalue or a const rvalue of T; and `rv` denotes a non-const rvalue of 520`T`. `Args` is a template parameter pack; `args` denotes a function parameter 521pack with the pattern `Args &&`. 522 523[heading Container] 524 525All containers must meet the requirements of this table: 526 527[table User-Defined Types and Operations for Containers 528 [[Expression] [Return Type] [Semantics] [Assertion/note/pre-/post-condition]] 529 [ 530 [ `X::value_type` ] 531 [ `T` ] 532 [ ] 533 [ Compile time only. ] 534 ] 535 [ 536 [ `X::reference` ] 537 [ `T &` ] 538 [ ] 539 [ Compile time only. ] 540 ] 541 [ 542 [ `X::const_reference` ] 543 [ `T const &` ] 544 [ ] 545 [ Compile time only. ] 546 ] 547 [ 548 [ `X::iterator` ] 549 [ An iterator whose `value_type` is `T`. ] 550 [ ] 551 [ Must meet the forward iterator requirements, and must be convertible to `X::const_iterator`. Compile time only. ] 552 ] 553 [ 554 [ `X::const_iterator` ] 555 [ A constant iterator whose `value_type` is `T`. ] 556 [ ] 557 [ Must meet the forward iterator requirements. Compile time only. ] 558 ] 559 [ 560 [ `X::difference_type` ] 561 [ A signed integer type. ] 562 [ ] 563 [ Identical to the diference type of `X::iterator` and `X::const_iterator`. Compile time only. ] 564 ] 565 [ 566 [ `X::size_type` ] 567 [ An unsigned integer type. ] 568 [ ] 569 [ Compile time only. ] 570 ] 571 [ 572 [ `X u;` ] 573 [ ] 574 [ ] 575 [ ['Ensures:] `u.empty()` ] 576 ] 577 [ 578 [ ``X u(a); 579X u = a; 580`` ] 581 [ ] 582 [ ] 583 [ ['Ensures:] `u == a` ] 584 ] 585 [ 586 [ ``X u(rv); 587X u = rv; 588`` ] 589 [ ] 590 [ ] 591 [ ['Ensures:] `u` is equal to the value `rv_c` had before this operation. ] 592 ] 593 [ 594 [ `a = rv` ] 595 [ `X &` ] 596 [ All existing elements of `a` are either move assigned to or destroyed. ] 597 [ ['Ensures:] `u` is equal to the value `rv_c` had before this operation. ] 598 ] 599 [ 600 [ `a.~X()` ] 601 [ ] 602 [ Destroys every element of `a`; any memory obtained is deallocated. ] 603 [ ] 604 ] 605 [ 606 [ `a.begin()` ] 607 [ `X::iterator` ] 608 [ ] 609 [ This is the non-`const` overload; the `const` overload is not needed. ] 610 ] 611 [ 612 [ `a.end()` ] 613 [ `X::iterator` ] 614 [ ] 615 [ This is the non-`const` overload; the `const` overload is not needed. ] 616 ] 617 [ 618 [ `a.swap(b)` ] 619 [ Convertible to `bool`. ] 620 [ Exchanges the contents of `a` and `b`. ] 621 [ ] 622 ] 623 [ 624 [ `r = a` ] 625 [ `X &` ] 626 [ ] 627 [ ['Ensures:] `r` == `a`. ] 628 ] 629 [ 630 [ `a.max_size()` ] 631 [ `X::size_type` ] 632 [ `std::distance(l.begin(), l.end())` for the largest possible container `l`. ] 633 [ ] 634 ] 635] 636 637[note The requirements above are taken from the standard. Even though the 638standard requires things to be a certain way, you can often define types that 639work in any context in which a container is supposed to work, even though it 640varies from the requirements above. In particular, you may want to have 641non-reference and non-pointer types for `X::reference` and `X::pointer`, 642respectively _emdash_ and that certainly will not break _cont_iface_. ] 643 644If you provide the types and operations above, _cont_iface_ will provide the 645rest of the container requirements, using this mapping: 646 647[table User-Defined Operations to sequence_container_interface Operations 648 [[User-Defined] [_cont_iface_-Provided] [Note]] 649 [ 650 [ ``a.begin() 651a.end()`` ] 652 [ ``a.empty() 653a.size() 654a.begin() 655a.end() 656a.cbegin() 657a.cend()`` ] 658 [ The user-defined `begin()` and `end()` are non-`const`, and the _cont_iface_-provided ones are `const`. _cont_iface_ can only provide `size()` if `X::const_iterator` is a random access iterator; otherwise, it must be user-defined. ] 659 ] 660 [ 661 [ `a == b` ] 662 [ `a != b` ] 663 [ Though `a == b` is provided by _cont_iface_, any user-defined replacement will be used to provide `a != b`. ] 664 ] 665 [ 666 [ `a.swap(b)` ] 667 [ `swap(a, b)` ] 668 [ ] 669 ] 670] 671 672[heading Reversible Container] 673 674Containers that are reverse-iterable must meet the requirements of this table 675(in addition to the container requirements): 676 677[table User-Defined Types and Operations for Reversible Containers 678 [[Expression] [Return Type] [Semantics] [Assertion/note/pre-/post-condition]] 679 [ 680 [ `X::reverse_iterator` ] 681 [ `boost::stl_interfaces::reverse_iterator<X::iterator>` ] 682 [ ] 683 [ Compile time only. ] 684 ] 685 [ 686 [ `X::const_reverse_iterator` ] 687 [ `boost::stl_interfaces::reverse_iterator<X::const_iterator>` ] 688 [ ] 689 [ Compile time only. ] 690 ] 691] 692 693If you provide the types and operations above, _cont_iface_ will provide the 694rest of the reversible container requirements, using this mapping: 695 696[table User-Defined Operations to sequence_container_interface Operations 697 [[User-Defined] [_cont_iface_-Provided] [Note]] 698 [ 699 [ ``a.begin() 700a.end()`` ] 701 [ ``a.rbegin() 702a.rend() 703a.crbegin() 704a.crend()`` ] 705 [ The user-defined `begin()` and `end()` are non-`const`, and _cont_iface_ provides both `const` and non-`const` overloads of `rbegin()` and `rend()`. _cont_iface_ can only provide these operations if `X::iterator` and `X::const_iterator` are bidirectional iterators. ] 706 ] 707] 708 709[heading Optional Container Operations] 710 711Containers that are comparable with `<`, `>`, `<=`, and `>=` get those 712operations automatically, so long as `T` is less-than comparable. In this 713case, there are no required user-defined operations, so that table is not 714needed. 715 716_cont_iface_ will provide the optional container requirements using this 717mapping: 718 719[table User-Defined Operations to sequence_container_interface Operations 720 [[User-Defined] [_cont_iface_-Provided] [Note]] 721 [ 722 [ `a < b` ] 723 [ ``a <= b 724a > b 725a >= b`` ] 726 [ Though `a < b` is provided by _cont_iface_, any user-defined replacement will be used to provide the other operations listed here. ] 727 ] 728] 729 730[heading Sequence Container] 731 732Sequence containers meet the requirements of this table (in addition to the 733container requirements): 734 735[table User-Defined Types and Operations for Sequence Containers 736 [[Expression] [Return Type] [Semantics] [Assertion/note/pre-/post-condition]] 737 [ 738 [ `X u(n, t);` ] 739 [ ] 740 [ Constructs a sequence of `n` copies of `t`. ] 741 [ ['Ensures:] `distance(u.begin(), u.end()) == n` ] 742 ] 743 [ 744 [ `X u(i, j);` ] 745 [ ] 746 [ Constructs a sequence equal to `[i, j)`. ] 747 [ ['Ensures:] `distance(u.begin(), u.end()) == distance(i, j)` ] 748 ] 749 [ 750 [ `X u(il);` ] 751 [ ] 752 [ `X u(il.begin(), il.end());` ] 753 [ ] 754 ] 755 [ 756 [ `a.emplace(p, args)` ] 757 [ `X::iterator` ] 758 [ Inserts an object of type T constructed with `std::forward<Args>(args)...` before `p`. ] 759 [ `args` may directly or indirectly refer to a value in `a`. ] 760 ] 761 [ 762 [ `a.insert(p, i, j)` ] 763 [ `X::iterator` ] 764 [ Inserts copies of the elements in `[i, j)` before `p`. ] 765 [ ] 766 ] 767 [ 768 [ `a.erase(q1, q2)` ] 769 [ `X::iterator` ] 770 [ Erases the elements in the range `[q1, q2)`. ] 771 [ ] 772 ] 773] 774 775[important In the notes for `a.emplace(p, args)`, it says: "`args` may 776directly or indirectly refer to a value in `a`". Don't forget to handle that 777case in your implementation. Otherwise, `a.emplace(a.begin(), a.back())` may 778do the wrong thing.] 779 780If you provide the types and operations above, _cont_iface_ will provide the 781rest of the sequence container requirements, using this mapping: 782 783[table User-Defined Operations to sequence_container_interface Operations 784 [[User-Defined] [_cont_iface_-Provided] [Note]] 785 [ 786 [ `X(il)` ] 787 [ `a = il` ] 788 [ ] 789 ] 790 [ 791 [ `a.emplace(p, args)` ] 792 [ 793``a.insert(p, t) 794a.insert(p, rv)`` ] 795 [ ] 796 ] 797 [ 798 [ `a.insert(p, i, j)` ] 799 [ ``a.insert(p, n, t) 800a.insert(p, il)`` ] 801 [ ] 802 ] 803 [ 804 [ `a.erase(q1, q2)` ] 805 [ ``a.erase(q) 806a.clear()`` ] 807 [ ] 808 ] 809 [ 810 [ ``a.erase(q1, q2) 811a.insert(p, i, j)`` ] 812 [ ``a.assign(i, j) 813a.assign(n, t) 814a.assign(il)`` ] 815 [ `a.erase(q1, q2)` and `a.insert(p, i, j)` must both be user-defined for _cont_iface_ to provide these operations. ] 816 ] 817] 818 819[heading Optional Sequence Container Operations] 820 821Sequence containers with `front()`, `back()`, or any of the other operations 822in this table must define these operations (in addition to the container 823requirements): 824 825[table User-Defined Types and Operations for Sequence Containers 826 [[Expression] [Return Type] [Semantics] [Assertion/note/pre-/post-condition]] 827 [ 828 [ `a.emplace_front(args)` ] 829 [ `X::reference` ] 830 [ Prepends an object of type `T` constructed with `std::forward<Args>(args)...`. ] 831 [ ] 832 ] 833 [ 834 [ `a.emplace_back(args)` ] 835 [ `X::reference` ] 836 [ Appends an object of type `T` constructed with `std::forward<Args>(args)...`. ] 837 [ ] 838 ] 839] 840 841If you provide the types and operations above, _cont_iface_ will provide the 842rest of the optional sequence container requirements, using this mapping: 843 844[table User-Defined Operations to sequence_container_interface Operations 845 [[User-Defined] [_cont_iface_-Provided] [Note]] 846 [ 847 [ `a.begin()` ] 848 [ ``a.front() 849a[n] 850a.at(n) 851`` ] 852 [ These operations are provided in `const` and non-`const` overloads. _cont_iface_ can only provide `a[n]` and `a.at(n)` if `X::iterator` and `X::const_iterator` are random access iterators. ] 853 ] 854 [ 855 [ `a.end()` ] 856 [ `a.back()` ] 857 [ `back()` is provided in `const` and non-`const` overloads. _cont_iface_ can only provide `a.back()` if `X::iterator` and `X::const_iterator` are bidirectional iterators. ] 858 ] 859 [ 860 [ `a.emplace_front(args)` ] 861 [ ``a.push_front(t) 862a.push_front(rv) 863`` ] 864 [ ] 865 ] 866 [ 867 [ `a.emplace_back(args)` ] 868 [ ``a.push_back(t) 869a.push_back(rv) 870`` ] 871 [ ] 872 ] 873 [ 874 [ ``a.emplace_front(args) 875a.erase(q1, q2)``] 876 [ `a.pop_front(t)` ] 877 [ `a.emplace_front(args)` and `a.erase(q1, q2)` must both be user-defined for _cont_iface_ to provide this operation. ] 878 ] 879 [ 880 [ ``a.emplace_back(args) 881a.erase(q1, q2)``] 882 [ `a.pop_back(t)` ] 883 [ `a.emplace_front(args)` and `a.erase(q1, q2)` must both be user-defined for _cont_iface_ to provide this operation. ] 884 ] 885] 886 887[note `emplace_front()` and `emplace_back()` are not needed for some of the 888_cont_iface_-provided operations above (e.g. `pop_front()` and `pop_back()`, 889respectively). However, they are each used as the user-defined operation that 890indicates that the container being defined is front- or 891back-mutation-friendly. ] 892 893[heading General Requirements on All User-Defined Operations] 894 895There are other requirements listed in the standard that do not appear in any 896of the requirements tables; user-defined operations must conform to those as 897well: 898 899* If an exception is thrown by an `insert()` or `emplace()` call while 900 inserting a single element, that function has no effect. 901* No `erase()` function throws an exception. 902* No copy constructor or assignment operator of a returned iterator throws an 903 exception. 904* The iterator returned from `a.emplace(p, args)` points to the new element 905 constructed from `args` into `a`. 906* The iterator returned from `a.insert(p, i, j)` points to the copy of the 907 first element inserted into `a`, or `p` if `i == j`. 908* The iterator returned by `a.erase(q1, q2)` points to the element pointed to 909 by `q2` prior to any elements being erased. If no such element exists, 910 `a.end()` is returned. 911 912[heading Example: `static_vector`] 913 914Let's look at an example. _Container_ contains a template called 915`boost::container::static_vector`, which is a fixed-capacity vector that does 916not allocate from the heap. We have a similar template in this example, 917`static_vector`. It is implemented by deriving from _cont_iface_, which 918provides much of the API specified in the STL, based on a subset of the API 919that the user must provide. 920 921`static_vector` meets all the sequence container requirements (including many 922of the optional ones) and reversible container requirements in the standard. 923It does not meet the allocator-aware container requirements, since it does not 924allocate. In short, it has the same full API as `std::vector`, without all 925the allocatory bits. 926 927[static_vector_defn] 928 929That's quite a bit of code. However, by using _cont_iface_, we were able to 930write only 22 functions, and let _cont_iface_ provide the other 39. 9 of the 93122 function that we did have to write were constructors and special member 932functions, and those always have to be written in the derived class; 933_cont_iface_ never could have helped with those. 934 935[note _cont_iface_ does not support all the sets of container requirements in 936the standard. In particular, it does not support the allocator-aware 937requirements, and it does not support the associative or unordered associative 938container requirements.] 939 940[endsect] 941 942[section Tutorial: `reverse_iterator`] 943 944There is a `std::reverse_iterator` template that has been around since C++98. 945In C++20 it is compatible with proxy iterators, and is `constexpr`- and 946`noexcept`-friendly. If you are using C++20 or later, just use 947`std::reverse_iterator`. For code built against earlier versions of C++, you 948can use _rev_iter_. 949 950There's nothing much to document about it; it works just like 951`std::reverse_iterator`. 952 953[endsect] 954