From 982b6057dae21195759c9c86b7a5533564fe08c9 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 11 Jul 2022 10:26:46 +0200 Subject: [PATCH 01/96] Added index, small wording fixes. --- book.tex | 12 +++++-- chapters/00_glossary.tex | 34 ------------------- chapters/02_introduction.tex | 22 ++++++------ chapters/03_algorithms_01_foreach.tex | 4 ++- chapters/03_algorithms_02_swaps.tex | 4 ++- chapters/03_algorithms_03_sorting.tex | 13 +++++++ chapters/03_algorithms_04_partitioning.tex | 5 +++ chapters/03_algorithms_05_divide.tex | 6 ++++ chapters/03_algorithms_06_linear_sorted.tex | 5 +++ chapters/03_algorithms_07_sets.tex | 4 +++ chapters/03_algorithms_08_transformations.tex | 14 +++++++- chapters/03_algorithms_09_left_folds.tex | 3 ++ .../03_algorithms_10_general_reductions.tex | 6 ++++ chapters/03_algorithms_11_boolean.tex | 3 ++ chapters/03_algorithms_12_generators.tex | 5 +++ chapters/03_algorithms_13_copies_moves.tex | 13 +++++++ chapters/03_algorithms_14_uninitialized.tex | 8 +++++ chapters/03_algorithms_15_heap.tex | 7 ++++ chapters/03_algorithms_16_searching.tex | 15 ++++++++ chapters/03_algorithms_17_minmax.tex | 8 ++++- chapters/06_cpp20_ranges_in_depth.tex | 2 +- chapters/XX_glossary.tex | 2 -- 22 files changed, 140 insertions(+), 55 deletions(-) delete mode 100644 chapters/00_glossary.tex delete mode 100644 chapters/XX_glossary.tex diff --git a/book.tex b/book.tex index cd56b76..7f70bb9 100644 --- a/book.tex +++ b/book.tex @@ -33,7 +33,6 @@ \frontmatter \input{chapters/00_title_page} -%\input{chapters/00_glossary}% TODO \input{chapters/01_preface} % In review \mainmatter @@ -41,6 +40,14 @@ \input{chapters/02_introduction} % In review \input{chapters/03_algorithms_00_main} % In review + +% Ranges +% Views +% Iterator adapters +% Parallel algorithms +% Making your own (Algorithm, View, Container) +% Related topics ??? + %\input{chapters/04_TODO} % TODO %\input{chapters/04_making_your_own} % TODO %\input{chapters/05_parallel_algorithms} % TODO @@ -48,8 +55,7 @@ %\input{chapters/07_cpp20_views}% TODO \appendix -%\input{chapters/XX_glossary} -%\input{chapters/XX_index} +\input{chapters/XX_index} \backmatter diff --git a/chapters/00_glossary.tex b/chapters/00_glossary.tex deleted file mode 100644 index 2886020..0000000 --- a/chapters/00_glossary.tex +++ /dev/null @@ -1,34 +0,0 @@ -\newglossaryentry{input iterator}{ - name=input iterator, - description={Is an iterator category that allows the following operations: advance and read (one pass)} -} - -\newglossaryentry{forward iterator}{ - name=forward iterator, - description={Is an iterator category that satisfies input iterator and allows elements to be read multiple times and out of order} -} - -\newglossaryentry{bidirectional iterator}{ - name=bidirectional iterator, - description={Is an iterator category that satisfies forward iterator and provides a move back operation (opposite direction of advance)} -} - -\newglossaryentry{random access iterator}{ - name=random access iterator, - description={Is an iterator category that satisfies bidirectional iterator and can advance and move back by any integer and calculate distance between two iterator, both in $O(1)$} -} - -\newglossaryentry{contiguous iterator}{ - name=contiguous iterator, - description={Is an iterator category that satisfies random access iterator and further guarantees that the underlying storage is continuous} -} - -\newglossaryentry{output iterator}{ - name=output iterator, - description={Is an iterator category that allows the following operations: advance and write (one pass)} -} - -\newglossaryentry{mutable iterator}{ - name=mutable iterator, - description={Name for any a combination of output iterator with any of input iterator categories} -} \ No newline at end of file diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index e4783c7..b255703 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -22,7 +22,7 @@ \section{History of standard \texorpdfstring{\CC}{C++} algorithms} \cppfile{code_examples/introduction/history_cc11_code.h} \end{box-note} -The \CC17 standard introduced parallel algorithms that provide an easy way to speed up processing with minimal effort. All you need to do is to specify the desired execution model, and the library will take care of spawning the correct number of threads. +The \CC17 standard introduced parallel algorithms that provide an easy way to speed up processing with minimal effort. All you need to do is to specify the desired execution model, and the library will take care of parallelizing the execution. \begin{box-note} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm using unsequenced parallel execution model. Note that counters are now shared state and need to be \cpp{std::atomic}\index{\cpp{std::atomic}} or protected by a \cpp{std::mutex}\index{\cpp{std::mutex}}. @@ -46,9 +46,9 @@ \section{Iterators and ranges} The \CC standard library solution to this problem are iterators and ranges. Iterators encapsulate implementation details of data structure traversal and simultaneously expose a set of operations possible on the given data structure in constant time and space. -A range is then denoted by a pair of iterators, or more generally, since \CC20, an iterator and a sentinel. +A range is then denoted by a pair of iterators, or more generally, since \CC20, an iterator and a sentinel. In mathematical terms, a pair of iterators \cpp{it1}, \cpp{it2} denotes a range \cpp{[it1, it2)}, that is, the range includes the element referenced by \cpp{it1} and ends before the element referenced by \cpp{it2}. -In mathematical terms, a pair of iterators \cpp{it1}, \cpp{it2} denotes a range \cpp{[it1, it2)}, that is, the range includes the element referenced by \cpp{it1} and ends before the element referenced by \cpp{it2}. +To reference the entire content of a data structure, we can use the \cpp{begin()} and \cpp{end()} methods that return an iterator to the first element and an iterator one past the last element, respectively. Hence, the range \cpp{[begin, end)} contains all data structure elements. \begin{box-note} \footnotesize Example of specifying a range using two iterators. @@ -72,13 +72,13 @@ \subsection{Iterator categories} \item[input/output iterator] read/write each element once, advance\\ \textit{data streams, e.g. writing/reading data to/from a network socket} \item[forward iterator] read/write each element repeatedly, advance\\ - \textit{singly-linked list, e.g. std::forward\_list} + \textit{singly-linked list, e.g. std::forward\_list}\index{\cpp{std::forward_list}} \item[bidirectional iterator] forward iterator + move back\\ - \textit{doubly-linked list, e.g. std::list, std::map, std::set} + \textit{doubly-linked list, e.g. std::list, std::map, std::set}\index{\cpp{std::list}}\index{\cpp{std::map}}\index{\cpp{std::set}} \item[random access iterator] bidirectional iterator + advance and move back by any integer and calculate distance between two iterators\\ - \textit{multi-array data structures, e.g. std::deque} + \textit{multi-array data structures, e.g. std::deque}\index{\cpp{std::deque}} \item[contiguous iterator] random access iterator + the storage of elements is contiguous\\ - \textit{arrays} + \textit{arrays, e.g. std::vector}\index{\cpp{std::vector}} \end{description} \begin{box-note} @@ -101,21 +101,21 @@ \subsection{Counted variants "\texttt{\_n}"} Since \CC20 we also have access to \cpp{std::counted_iterator} adapter, which will only work with the range versions of algorithms.\\ -\noindent Examples: \cpp{std::for_each_n}, \cpp{std::copy_n}\\ +\noindent Examples: \cpp{std::for_each_n}, \cpp{std::copy_n}\\\index{\cpp{std::for_each_n}}\index{\cpp{std::copy_n}} -\noindent Note: while \cpp{std::search_n} does follow the naming, it does not follow the same semantics. The \texttt{\_n} here refers to the number of instances of the searched element. +\noindent Note: while \cpp{std::search_n} does follow the naming, it does not follow the same semantics. The \texttt{\_n} here refers to the number of instances of the searched element.\index{\cpp{std::search_n}} \subsection{Copy variants "\texttt{\_copy}"} Copy variants of in-place algorithms that output the result through the provided iterator. The copy behaviour allows this variant to operate on immutable ranges.\\ -\noindent Examples: \cpp{std::remove_copy}, \cpp{std::partial_sort_copy} +\noindent Examples: \cpp{std::remove_copy}, \cpp{std::partial_sort_copy}\index{\cpp{std::remove_copy}}\index{\cpp{std::partial_sort_copy}} \subsection{Predicate variants "\texttt{\_if}"} Predicate variants of algorithms use a predicate to determine a "match" instead of comparing against a value. The standard also has one instance of \texttt{\_if\_not} variant that inverts the predicate logic (\cpp{false} is treated as a match).\\ -\noindent Examples: \cpp{std::find_if}, \cpp{std::replace_if} +\noindent Examples: \cpp{std::find_if}, \cpp{std::replace_if}\index{\cpp{std::find_if}}\index{\cpp{std::replace_if}} \subsection{Restrictions on invocable} diff --git a/chapters/03_algorithms_01_foreach.tex b/chapters/03_algorithms_01_foreach.tex index c187b37..f068ace 100644 --- a/chapters/03_algorithms_01_foreach.tex +++ b/chapters/03_algorithms_01_foreach.tex @@ -7,11 +7,12 @@ \section{Introducing the algorithms} \begin{enumerate}[label=\protect\circled{\arabic*}] \item The presentation of each algorithm will start with a short description. \item The margin will contain information about the history of the algorithm: which C++ standard introduced it and whether it has constexpr, parallel and range variants and including versions of the standard that introduced them. - \item Following that will be the description of constraints. Algorithms that do not operate inline are denoted using an arrow: \cpp{input_range -> output_range}. + \item Following that will be the description of constraints. Algorithms that write data to a distinct output range are denoted with an arrow: \cpp{input_range -> output_range}. \item Finally, each description will conclude with one or more examples with explanations. \end{enumerate} \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} +\index{\cpp{std::for_each}} \circled{1} The \cpp{std::for_each} algorithm applies the provided invocable to each element of the range in order. If the underlying range is mutable, the invocable is permitted to change the state of elements but cannot invalidate iterators. @@ -68,6 +69,7 @@ \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::for_each_n}}{\texttt{std::for\_each\_n}}} +\index{\cpp{std::for_each_n}} \circled{1} The \cpp{std::for_each_n} algorithm applies the provided invocable to each element of the range specified using an iterator and the number of elements. If the underlying range is mutable, the invocable is permitted to change the state of elements but cannot invalidate iterators. diff --git a/chapters/03_algorithms_02_swaps.tex b/chapters/03_algorithms_02_swaps.tex index 57ee4c4..ecfabde 100644 --- a/chapters/03_algorithms_02_swaps.tex +++ b/chapters/03_algorithms_02_swaps.tex @@ -29,6 +29,7 @@ \section{Swaps} \end{box-note} \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} +\index{\cpp{std::swap}} The non-range version of \cpp{std::swap} will swap the values of the two parameters using a three-step move-swap. Users can provide a more optimized implementation as friend functions on their type. @@ -58,6 +59,7 @@ \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::iter_swap}}{\texttt{std::iter\_swap}}} +\index{\cpp{std::iter_swap}} The \cpp{std::iter_swap} is an indirect swap, swapping values behind two forward iterators. @@ -80,12 +82,12 @@ \subsection{\texorpdfstring{\cpp{std::iter_swap}}{\texttt{std::iter\_swap}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::swap_ranges}}{\texttt{std::swap\_ranges}}} +\index{\cpp{std::swap_ranges}} The \cpp{std::swap_ranges} algorithm exchanges elements between two non-overlapping ranges (potentially from the same container). \cppversions{\texttt{swap\_ranges}}{\CC98}{\CC20}{\CC17}{\CC20} - \begin{box-note} \footnotesize Example of swapping the first three elements of an array with the last three elements using \cpp{std::swap_ranges}. Note the reversed order of elements due to the use of \cpp{rbegin}. \tcblower diff --git a/chapters/03_algorithms_03_sorting.tex b/chapters/03_algorithms_03_sorting.tex index 5f50ed7..66f5ae7 100644 --- a/chapters/03_algorithms_03_sorting.tex +++ b/chapters/03_algorithms_03_sorting.tex @@ -1,6 +1,7 @@ \section{Sorting} Before we talk about sorting, we need to discuss what the standard requires for types to be comparable—specifically, the \cpp{strict_weak_ordering} required by sorting algorithms. +\index{\cpp{strict_weak_ordering}} Implementing a \cpp{strict_weak_ordering} for a custom type at minimum requires providing an overload of \cpp{operator<} with the following behaviour: \begin{itemize} @@ -27,8 +28,12 @@ \section{Sorting} \item \cpp{std::weak_ordering} \item \cpp{std::partial_ordering} \end{itemize} +\index{\cpp{std::strong_ordering}} +\index{\cpp{std::weak_ordering}} +\index{\cpp{std::partial_ordering}} \subsection{\texorpdfstring{\cpp{std::lexicographical_compare}}{\texttt{std::lexicographical\_compare}}} +\index{\cpp{std::lexicographical_compare}} Lexicographical \texttt{strict\_weak\_ordering} for ranges is exposed through the \texttt{std::lexicographical\_compare} algorithm. @@ -51,6 +56,7 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare}}{\texttt{std::lex \end{box-note} \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\texttt{std::lexicographical\_compare\_three\_way}}} +\index{\cpp{std::lexicographical_compare_three_way}} The \texttt{std::lexicographical\_compare\_three\_way} is the spaceship operator equivalent to \texttt{std::lexicographical\_compare}. It returns one of \texttt{std::strong\_ordering}, \texttt{std::weak\_ordering} or \texttt{std::partial\_ordering} types, depending on the type returned by the elements' spaceship operator. @@ -64,6 +70,7 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\textt \end{box-note} \subsection{\texorpdfstring{\cpp{std::sort}}{\texttt{std::sort}}} +\index{\cpp{std::sort}} The \cpp{std::sort} algorithm is the canonical $O(n*logn)$ sort (typically implemented as intro-sort). @@ -89,6 +96,7 @@ \subsection{\texorpdfstring{\cpp{std::sort}}{\texttt{std::sort}}} Before C++14, you would have to fully specify the type of the comparator, i.e. \cpp{std::greater{}}. The type erased variant \cpp{std::greater<>{}} relies on type deduction to determine the parameter types. Projections accept an unary invocable, including pointers to members and member functions. \subsection{\texorpdfstring{\cpp{std::stable_sort}}{\texttt{std::stable\_sort}}} +\index{\cpp{std::stable_sort}} The \cpp{std::sort} is free to re-arrange equivalent elements, which can be undesirable when re-sorting an already sorted range. The \cpp{std::stable_sort} provides the additional guarantee of preserving the relative order of equal elements. @@ -104,6 +112,7 @@ \subsection{\texorpdfstring{\cpp{std::stable_sort}}{\texttt{std::stable\_sort}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::is_sorted}}{\texttt{std::is\_sorted}}} +\index{\cpp{std::is_sorted}} The \cpp{std::is_sorted} algorithm is a linear check returning a boolean denoting whether the ranges elements are in non-descending order. @@ -117,6 +126,7 @@ \subsection{\texorpdfstring{\cpp{std::is_sorted}}{\texttt{std::is\_sorted}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::is_sorted_until}}{\texttt{std::is\_sorted\_until}}} +\index{\cpp{std::is_sorted_until}} The \cpp{std::is_sorted_until} algorithm returns the first out-of-order element in the given range, thus denoting a sorted sub-range. @@ -133,6 +143,7 @@ \subsection{\texorpdfstring{\cpp{std::is_sorted_until}}{\texttt{std::is\_sorted\ \small\cpp{std::is_sorted(r.begin(), std::is_sorted_until(r.begin(), r.end()))} \subsection{\texorpdfstring{\cpp{std::partial_sort}}{\texttt{std::partial\_sort}}} +\index{\cpp{std::partial_sort}} The \cpp{std::partial_sort} algorithm reorders the range's elements such that the leading sub-range is in the same order it would when fully sorted. However, the algorithm leaves the rest of the range in an unspecified order. @@ -149,6 +160,7 @@ \subsection{\texorpdfstring{\cpp{std::partial_sort}}{\texttt{std::partial\_sort} \end{box-note} \subsection{\texorpdfstring{\cpp{std::partial_sort_copy}}{\texttt{std::partial\_sort\_copy}}} +\index{\cpp{std::partial_sort_copy}} The \cpp{std::partial_sort_copy} algorithm has the same behaviour as \cpp{std::partial_sort}; however, it does not operate inline. Instead, the algorithm writes the results to a second range. @@ -164,6 +176,7 @@ \subsection{\texorpdfstring{\cpp{std::partial_sort_copy}}{\texttt{std::partial\_ \end{box-note} \subsection{\texorpdfstring{\cpp{qsort}}{\texttt{qsort}} - C standard library} +\index{\cpp{qsort}} Because the C standard library is part of the C++ standard library, we also have access to \cpp{qsort}. diff --git a/chapters/03_algorithms_04_partitioning.tex b/chapters/03_algorithms_04_partitioning.tex index 15e7686..c1d4e96 100644 --- a/chapters/03_algorithms_04_partitioning.tex +++ b/chapters/03_algorithms_04_partitioning.tex @@ -5,6 +5,7 @@ \section{Partitioning} Partitioning comes up often when we need to group elements based on a particular property. You can also think of partitioning as equal to sorting if we would sort based on the values of a boolean property. \subsection{\texorpdfstring{\cpp{std::partition}}{\texttt{std::partition}}} +\index{\cpp{std::partition}} The \cpp{std::partition} algorithm provides the basic partitioning functionality, reordering elements based on a unary predicate. The algorithm returns the partition point, an iterator to the first element for which the predicate returned \cpp{false}. @@ -18,6 +19,7 @@ \subsection{\texorpdfstring{\cpp{std::partition}}{\texttt{std::partition}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::stable_partition}}{\texttt{std::stable\_partition}}} +\index{\cpp{std::stable_partition}} The \cpp{std::partition} algorithm is permitted to rearrange the elements with the only guarantee that elements for which the predicate evaluated to \cpp{true} will precede elements for which the predicate evaluated to \cpp{false}. This behaviour can be undesirable, for example, for UI elements. @@ -34,6 +36,7 @@ \subsection{\texorpdfstring{\cpp{std::stable_partition}}{\texttt{std::stable\_pa \end{box-note} \subsection{\texorpdfstring{\cpp{std::is_partitioned}}{\texttt{std::is\_partitioned}}} +\index{\cpp{std::is_partitioned}} The \cpp{std::is_partitioned} algorithm is a linear check returning a boolean denoting whether the ranges elements are partitioned in regards to the predicate. @@ -49,6 +52,7 @@ \subsection{\texorpdfstring{\cpp{std::is_partitioned}}{\texttt{std::is\_partitio \end{box-note} \subsection{\texorpdfstring{\cpp{std::partition_copy}}{\texttt{std::partition\_copy}}} +\index{\cpp{std::partition_copy}} The \cpp{std::partition_copy} is a variant of \cpp{std::partition} that, instead of reordering elements, will output the partitioned elements to the two output ranges denoted by two iterators. @@ -62,6 +66,7 @@ \subsection{\texorpdfstring{\cpp{std::partition_copy}}{\texttt{std::partition\_c \end{box-note} \subsection{\texorpdfstring{\cpp{std::nth_element}}{\texttt{std::nth\_element}}} +\index{\cpp{std::nth_element}} The \cpp{nth_element} algorithm is a partitioning algorithm that ensures that the element in the nth position is the element that would be in this position if the range was sorted. diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index d983151..b560bc6 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -7,6 +7,8 @@ \section{Divide and conquer} Divide and conquer algorithms allow the lookup of bounds based on strict weak ordering and work even when the container’s specific value is not present. Additionally, since we are working with a sorted container, we can easily access neighbouring values once we have determined a boundary. \subsection{\texorpdfstring{\cpp{std::lower_bound}, \cpp{std::upper_bound}}{\texttt{std::lower\_bound}, \texttt{std::upper\_bound}}} +\index{\cpp{std::lower_bound}} +\index{\cpp{std::upper_bound}} The \cpp{std::lower_bound} and \cpp{std::upper_bound} algorithms offer boundary search with logarithmic complexity (for random access ranges. @@ -37,6 +39,7 @@ \subsection{\texorpdfstring{\cpp{std::lower_bound}, \cpp{std::upper_bound}}{\tex \end{box-note} \subsection{\texorpdfstring{\cpp{std::equal_range}}{\texttt{std::equal\_range}}} +\index{\cpp{std::equal_range}} The \cpp{std::equal_range} algorithm returns both lower and upper bounds for the given value. @@ -52,6 +55,7 @@ \subsection{\texorpdfstring{\cpp{std::equal_range}}{\texttt{std::equal\_range}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::partition_point}}{\texttt{std::partition\_point}}} +\index{\cpp{std::partition_point}} Despite the naming, \cpp{std:partition_point} works very similarly to \cpp{std::upper_bound}, however instead of searching for a particular value, it searches using a predicate. @@ -67,6 +71,7 @@ \subsection{\texorpdfstring{\cpp{std::partition_point}}{\texttt{std::partition\_ \end{box-note} \subsection{\texorpdfstring{\cpp{std::binary_search}}{\texttt{std::binary\_search}}} +\index{\cpp{std::binary_search}} The \cpp{std::binary_search} provides a presence check, returning a boolean indicating whether the requested value is present in the sorted range or not. @@ -82,6 +87,7 @@ \subsection{\texorpdfstring{\cpp{std::binary_search}}{\texttt{std::binary\_searc \end{box-note} \subsection{\texorpdfstring{\cpp{bsearch} - C standard library}{\texttt{bsearch} - C standard library}} +\index{\cpp{std::bsearch}} From the C standard library, C++ inherits \cpp{bsearch}. This algorithm returns one of the elements equal to the provided key, or \cpp{nullptr} if none such element is found. diff --git a/chapters/03_algorithms_06_linear_sorted.tex b/chapters/03_algorithms_06_linear_sorted.tex index 62f8e54..ff5767c 100644 --- a/chapters/03_algorithms_06_linear_sorted.tex +++ b/chapters/03_algorithms_06_linear_sorted.tex @@ -3,6 +3,7 @@ \section{Linear operations on sorted ranges} In this section, we will discuss algorithms operating on sorted ranges in linear time. The same functionality on unsorted ranges would require algorithms operating in quadratic time. \subsection{\texorpdfstring{\cpp{std::includes}}{\texttt{std::includes}}} +\index{\cpp{std::includes}} The \cpp{std::includes} algorithm will determine whether one range (all elements) is contained within another range. @@ -18,6 +19,7 @@ \subsection{\texorpdfstring{\cpp{std::includes}}{\texttt{std::includes}}} The example uses \cpp{std::iota} to generate the lower-case letters (line 2), which requires the destination vector to be pre-allocated (line 1). \subsection{\texorpdfstring{\cpp{std::merge}}{\texttt{std::merge}}} +\index{\cpp{std::merge}} The \cpp{std::merge} algorithm merges two sorted ranges, with the output written to a third range which cannot overlap with either of the input ranges. @@ -41,6 +43,7 @@ \subsection{\texorpdfstring{\cpp{std::merge}}{\texttt{std::merge}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::inplace_merge}}{\texttt{std::inplace\_merge}}} +\index{\cpp{std::inplace_merge}} The \cpp{std::inplace_merge} algorithm merges two consecutive sub-ranges. @@ -56,6 +59,8 @@ \subsection{\texorpdfstring{\cpp{std::inplace_merge}}{\texttt{std::inplace\_merg \end{box-note} \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{std::unique}, \texttt{std::unique\_copy}}} +\index{\cpp{std::unique}} +\index{\cpp{std::unique_copy}} The std::unique algorithm removes consecutive duplicate values. The typical use case is in conjunction with a sorted range. However, this is not a requirement of std::unique. diff --git a/chapters/03_algorithms_07_sets.tex b/chapters/03_algorithms_07_sets.tex index 6a7f333..2fbcbf9 100644 --- a/chapters/03_algorithms_07_sets.tex +++ b/chapters/03_algorithms_07_sets.tex @@ -3,6 +3,7 @@ \section{Set operations} The group of set algorithms simulates different set operations on two sorted ranges. \subsection{\texorpdfstring{\cpp{std::set_difference}}{\texttt{std::set\_difference}}} +\index{\cpp{std::set_difference}} The \cpp{std::set_difference} algorithm produces a range containing elements present in the first range but not in the second range. @@ -24,6 +25,7 @@ \subsection{\texorpdfstring{\cpp{std::set_difference}}{\texttt{std::set\_differe \end{box-note} \subsection{\texorpdfstring{\cpp{std::set_symmetric_difference}}{\texttt{std::set\_symmetric\_difference}}} +\index{\cpp{std::set_symmetric_difference}} The \cpp{std::set_symetric_difference} algorithm produces a range containing elements only present in one of the ranges, but not both. @@ -45,6 +47,7 @@ \subsection{\texorpdfstring{\cpp{std::set_symmetric_difference}}{\texttt{std::se \end{box-note} \subsection{\texorpdfstring{\cpp{std::set_union}}{\texttt{std::set\_union}}} +\index{\cpp{std::set_union}} The \cpp{std::set_union} algorithm produces a range containing elements present in either of the ranges. @@ -66,6 +69,7 @@ \subsection{\texorpdfstring{\cpp{std::set_union}}{\texttt{std::set\_union}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::set_intersection}}{\texttt{std::set\_intersection}}} +\index{\cpp{std::set_intersection}} The \cpp{std::set_intersection} algorithm produces a range containing elements present in both of the ranges. diff --git a/chapters/03_algorithms_08_transformations.tex b/chapters/03_algorithms_08_transformations.tex index cc9e8e5..1dbf64d 100644 --- a/chapters/03_algorithms_08_transformations.tex +++ b/chapters/03_algorithms_08_transformations.tex @@ -3,6 +3,7 @@ \section{Transformation algorithms} In this section, we will discuss algorithms that transform ranges by changing the values of elements and removing and re-ordering elements. \subsection{\texorpdfstring{\cpp{std::transform}}{\texttt{std::transform}}} +\index{\cpp{std::transform}} The most straightforward transformation possible is to apply a transformation function to each element. The \cpp{std::transform} algorithm provides this functionality in unary and binary variants (input from one or two ranges). @@ -21,6 +22,7 @@ \subsection{\texorpdfstring{\cpp{std::transform}}{\texttt{std::transform}}} Note that \cpp{std::transform} does not guarantee strict left-to-right evaluation. If that is required, use \cpp{std::for_each} instead. \subsection{\texorpdfstring{\cpp{std::adjacent_difference}}{\texttt{std::adjacent\_difference}}} +\index{\cpp{std::adjacent_difference}} The \cpp{std::adjacent_difference} is a numerical algorithm, which might make it seem out of place in this section; however, it is essentially a binary transformation algorithm that operates on adjacent elements of the single source range. @@ -44,6 +46,8 @@ \subsection{\texorpdfstring{\cpp{std::adjacent_difference}}{\texttt{std::adjacen Note that the \cpp{std::next(data.begin())} for the output range is critical here. The \cpp{std::adjacent_difference} algorithm will read each element only once and remembers the previously read value for the left argument. The \cpp{std::next} ensures that we generate one element ahead of either argument. \subsection{\texorpdfstring{\cpp{std::remove}, \cpp{std::remove_if}}{\texttt{std::remove}, \texttt{std::remove\_if}}} +\index{\cpp{std::remove}} +\index{\cpp{std::remove_if}} The \cpp{std::remove} and \cpp{std::remove_if} algorithms "remove" elements that match the given value or for which the given predicate evaluates to true. @@ -58,7 +62,9 @@ \subsection{\texorpdfstring{\cpp{std::remove}, \cpp{std::remove_if}}{\texttt{std \cppfile{code_examples/algorithms/remove_code.h} \end{box-note} -\subsection{\texorpdfstring{\cpp{std::remove}, \cpp{std::remove_if}}{\texttt{std::remove}, \texttt{std::remove\_if}}} +\subsection{\texorpdfstring{\cpp{std::replace}, \cpp{std::replace_if}}{\texttt{std::replace}, \texttt{std::replace\_if}}} +\index{\cpp{std::replace}} +\index{\cpp{std::replace_if}} The \cpp{std::replace} and \cpp{std::replace_if} algorithms replace elements that match the given value or for which the given predicate evaluates to true. @@ -72,6 +78,7 @@ \subsection{\texorpdfstring{\cpp{std::remove}, \cpp{std::remove_if}}{\texttt{std \end{box-note} \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} +\index{\cpp{std::reverse}} The \cpp{std::reverse} algorithm will reverse the order of elements in the range by applying \cpp{std::swap} to pairs of elements. @@ -95,6 +102,7 @@ \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} +\index{\cpp{std::rotate}} The \cpp{std::rotate} algorithm rearranges elements in the range from \cpp{[first, middle),[middle, last)} to \cpp{[middle, last),[first, middle)}. @@ -108,6 +116,7 @@ \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::shuffle}}{\texttt{std::shuffle}}} +\index{\cpp{std::shuffle}} The \cpp{std::shuffle} algorithm is a successor of the now-defunct \cpp{std::random_shuffle} algorithm (deprecated in \CC14, removed in \CC17) and relies on new random facilities added in \CC11. @@ -121,6 +130,8 @@ \subsection{\texorpdfstring{\cpp{std::shuffle}}{\texttt{std::shuffle}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::next_permutation}, \cpp{std::prev_permutation}}{\texttt{std::next\_permutation}, \texttt{std::prev\_permutation}}} +\index{\cpp{std::next_permutation}} +\index{\cpp{std::prev_permutation}} The \cpp{std::next_permutation} and \cpp{std::prev_permutation} algorithms will rearrange the element so that they are in their next or previous (by lexicographical comparison) permutation. When there is no such ordering, both algorithms will wrap around, but will also return \cpp{false} to notify the caller. @@ -135,6 +146,7 @@ \subsection{\texorpdfstring{\cpp{std::next_permutation}, \cpp{std::prev_permutat \end{box-note} \subsection{\texorpdfstring{\cpp{std::is_permutation}}{\texttt{std::is\_permutation}}} +\index{\cpp{std::is_permutation}} The \cpp{std::is_permutation} algorithm is an excellent tool for checking whether two ranges have the same content but not necessarily the same order of elements. diff --git a/chapters/03_algorithms_09_left_folds.tex b/chapters/03_algorithms_09_left_folds.tex index d3734a0..61c0da8 100644 --- a/chapters/03_algorithms_09_left_folds.tex +++ b/chapters/03_algorithms_09_left_folds.tex @@ -5,6 +5,7 @@ \section{Left folds} Because of the strictly linear operation, none of the left-fold algorithms supports a parallel version. \subsection{\texorpdfstring{\cpp{std::accumulate}}{\texttt{std::accumulate}}} +\index{\cpp{std::accumulate}} The \cpp{std::accumulate} algorithm is the single-range left-fold. @@ -27,6 +28,7 @@ \subsection{\texorpdfstring{\cpp{std::accumulate}}{\texttt{std::accumulate}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::inner_product}}{\texttt{std::inner\_product}}} +\index{\cpp{std::inner_product}} The \cpp{std::inner_product} algorithm is a left fold over two ranges. The pairs of elements are first reduced and then accumulated. @@ -50,6 +52,7 @@ \subsection{\texorpdfstring{\cpp{std::inner_product}}{\texttt{std::inner\_produc \end{box-note} \subsection{\texorpdfstring{\cpp{std::partial_sum}}{\texttt{std::partial_sum}}} +\index{\cpp{std::partial_sum}} The \cpp{std::partial_sum} algorithm operates as a left fold. However, it doesn't reduce the range into a single value. Instead, the algorithm writes each partial result to the output range. diff --git a/chapters/03_algorithms_10_general_reductions.tex b/chapters/03_algorithms_10_general_reductions.tex index 87b0f8c..450a5a3 100644 --- a/chapters/03_algorithms_10_general_reductions.tex +++ b/chapters/03_algorithms_10_general_reductions.tex @@ -7,6 +7,7 @@ \section{General reductions} This is why with the parallel support in C++17, we also received a batch of generalised reduction algorithms that reduce elements in unspecified order and permutation. \subsection{\texorpdfstring{\cpp{std::reduce}}{\texttt{std::reduce}}} +\index{\cpp{std::reduce}} The \cpp{std::reduce} algorithm is a generalised version of \cpp{std::accumulate}. That is, it reduces a range by applying the provided accumulation operation to the elements in unspecified order and permutation. @@ -32,6 +33,7 @@ \subsection{\texorpdfstring{\cpp{std::reduce}}{\texttt{std::reduce}}} The initial value of the accumulator will be “Quack” (line 2). Adding the other two ducks (line 8), we end up with “QuackQuackQuack”. \subsection{\texorpdfstring{\cpp{std::transform_reduce}}{\texttt{std::transform\_reduce}}} +\index{\cpp{std::transform_reduce}} The \cpp{std::transform_reduce} algorithm is the generalised counterpart to \cpp{std::inner_product}. On top of the two-range variant, the algorithm also provides a unary overload. @@ -46,6 +48,8 @@ \subsection{\texorpdfstring{\cpp{std::transform_reduce}}{\texttt{std::transform\ \end{box-note} \subsection{\texorpdfstring{\cpp{std::inclusive_scan}, \cpp{std::exclusive_scan}}{\texttt{std::inclusive\_scan}, \texttt{std::exclusive\_scan}}} +\index{\cpp{std::inclusive_scan}} +\index{\cpp{std::exclusive_scan}} The \cpp{std::inclusive_scan} is a generalised version of \cpp{std::partial_sum}. On top of that, we also have access to \cpp{std::exclusive_scan}. @@ -71,6 +75,8 @@ \subsection{\texorpdfstring{\cpp{std::inclusive_scan}, \cpp{std::exclusive_scan} \end{box-note} \subsection{\texorpdfstring{\cpp{std::transform_inclusive_scan},\newline\cpp{std::transform_exclusive_scan}}{\texttt{std::transform\_inclusive\_scan},\newline \texttt{std::transform\_exclusive\_scan}}} +\index{\cpp{std::transform_inclusive_scan}} +\index{\cpp{std::transform_exclusive_scan}} The \cpp{std::transform_inclusive_scan} and \cpp{std::transform_exclusive_scan} algorithms are variants of \cpp{std::inclusive_scan} and \cpp{std::exclusive_scan} that apply a unary transformation function to each element before the reduction operation. diff --git a/chapters/03_algorithms_11_boolean.tex b/chapters/03_algorithms_11_boolean.tex index 05f20b6..f0d1024 100644 --- a/chapters/03_algorithms_11_boolean.tex +++ b/chapters/03_algorithms_11_boolean.tex @@ -3,6 +3,9 @@ \section{Boolean reductions} When reducing boolean expressions, we can take advantage of the early termination offered by boolean logic. The standard offers a set of three boolean reduction algorithms. \subsection{\texorpdfstring{\cpp{std::all_of}, \cpp{std::any_of}, \cpp{std::none_of}}{\texttt{std::all\_of}, \texttt{std::any\_of}, \texttt{std::none\_of}}} +\index{\cpp{std::all_of}} +\index{\cpp{std::any_of}} +\index{\cpp{std::none_of}} The algorithms either require the elements to be convertible to bool or a predicate to be specified. diff --git a/chapters/03_algorithms_12_generators.tex b/chapters/03_algorithms_12_generators.tex index 784d901..9919ea6 100644 --- a/chapters/03_algorithms_12_generators.tex +++ b/chapters/03_algorithms_12_generators.tex @@ -3,6 +3,8 @@ \section{Generators} The C++ standard offers three types of generators: fill with copies of a value, fill with results of invoking a generator functor and fill with sequentially increasing values. \subsection{\texorpdfstring{\cpp{std::fill}, \cpp{std::generate}}{\texttt{std::fill}, \texttt{std::generate}}} +\index{\cpp{std::fill}} +\index{\cpp{std::generate}} The \cpp{std::fill} algorithm fills a range by consecutively assigning the given value to each element. The \cpp{std::generate} algorithm fills a range by consecutively assigning the result of the provided generator. @@ -20,6 +22,8 @@ \subsection{\texorpdfstring{\cpp{std::fill}, \cpp{std::generate}}{\texttt{std::f \end{box-note} \subsection{\texorpdfstring{\cpp{std::fill_n}, \cpp{std::generate_n}}{\texttt{std::fill\_n}, \texttt{std::generate\_n}}} +\index{\cpp{std::fill_n}} +\index{\cpp{std::generate_n}} The \cpp{std::fill_n} and \cpp{std::generate_n} are variants of \cpp{std::fill} and \cpp{std::generate} that operate on ranges specified using the start iterator and number of elements. This behaviour allows the algorithms to be used with iterator adapters, such as \cpp{std::back_inserter}. @@ -33,6 +37,7 @@ \subsection{\texorpdfstring{\cpp{std::fill_n}, \cpp{std::generate_n}}{\texttt{st \end{box-note} \subsection{\texorpdfstring{\cpp{std::iota}}{\texttt{std::iota}}} +\index{\cpp{std::iota}} The \cpp{std::iota} generates elements by consecutively assigning the result of applying the prefix \cpp{operator++}, starting with the initial value. diff --git a/chapters/03_algorithms_13_copies_moves.tex b/chapters/03_algorithms_13_copies_moves.tex index f12c313..a4c6ec1 100644 --- a/chapters/03_algorithms_13_copies_moves.tex +++ b/chapters/03_algorithms_13_copies_moves.tex @@ -3,6 +3,8 @@ \section{Copy and move} The standard offers a wide range of copy algorithms in roughly three categories: simple copies and moves, selective copies and copies with reordering. \subsection{\texorpdfstring{\cpp{std::copy}, \cpp{std::move}}{\texttt{std::copy}, \texttt{std::move}}} +\index{\cpp{std::copy}} +\index{\cpp{std::move}} The \cpp{std::copy} and \cpp{std::move} algorithms provide a forward copy and move. The direction is important for overlapping ranges, so we do not overwrite the yet-to-be copied elements. @@ -36,6 +38,8 @@ \subsection{\texorpdfstring{\cpp{std::copy}, \cpp{std::move}}{\texttt{std::copy} \end{box-note} \subsection{\texorpdfstring{\cpp{std::copy_backward}, \cpp{std::move_backward}}{\texttt{std::copy\_backward}, \texttt{std::move\_backward}}} +\index{\cpp{std::copy_backward}} +\index{\cpp{std::move_backward}} The \cpp{std::copy_backward} and \cpp{std::move_backward} are variants that copy in the opposite direction, starting at the back of the range. Because of this, the head of the output range can now overlap with the input range. @@ -53,6 +57,7 @@ \subsection{\texorpdfstring{\cpp{std::copy_backward}, \cpp{std::move_backward}}{ \end{box-note} \subsection{\texorpdfstring{\cpp{std::copy_n}}{\texttt{std::copy\_n}}} +\index{\cpp{std::copy_n}} The \cpp{std::copy_n} algorithm is the counted variant of \cpp{std::copy} that accepts an input range specified using an iterator and the number of elements. @@ -68,6 +73,9 @@ \subsection{\texorpdfstring{\cpp{std::copy_n}}{\texttt{std::copy\_n}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::copy_if}, \cpp{std::remove_copy}, \cpp{std::remove_copy_if}}{\texttt{std::copy\_if}, \texttt{std::remove\_copy}, \texttt{std::remove\_copy\_if}}} +\index{\cpp{std::copy_if}} +\index{\cpp{std::remove_copy}} +\index{\cpp{std::remove_copy_if}} The \cpp{std::copy_if}, \cpp{std::remove_copy} and \cpp{std::remove_copy_if} are selective copy algorithms. @@ -86,6 +94,7 @@ \subsection{\texorpdfstring{\cpp{std::copy_if}, \cpp{std::remove_copy}, \cpp{std \end{box-note} \subsection{\texorpdfstring{\cpp{std::sample}}{\texttt{std::sample}}} +\index{\cpp{std::sample}} The \cpp{std::sample} algorithm is a random selective copy algorithm. The algorithm will copy a random selection of N elements from the source range to the destination range utilising the provided random number generator. @@ -101,6 +110,8 @@ \subsection{\texorpdfstring{\cpp{std::sample}}{\texttt{std::sample}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::replace_copy}, \cpp{std::replace_copy_if}}{\texttt{std::replace\_copy}, \texttt{std::replace\_copy\_if}}} +\index{\cpp{std::replace_copy}} +\index{\cpp{std::replace_copy_if}} The \cpp{std::replace_copy} and \cpp{std::replace_copy_if} algorithms operate like \cpp{std::copy}; however, they will copy a provided value instead of specific elements. @@ -117,6 +128,7 @@ \subsection{\texorpdfstring{\cpp{std::replace_copy}, \cpp{std::replace_copy_if}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::reverse_copy}}{\texttt{std::reverse\_copy}}} +\index{\cpp{std::reverse_copy}} The \cpp{std::reverse_copy} algorithm copies elements in reverse order. @@ -132,6 +144,7 @@ \subsection{\texorpdfstring{\cpp{std::reverse_copy}}{\texttt{std::reverse\_copy} \end{box-note} \subsection{\texorpdfstring{\cpp{std::rotate_copy}}{\texttt{std::rotate\_copy}}} +\index{\cpp{std::rotate_copy}} The \cpp{std::rotate_copy} algorithm will copy elements \cpp{[middle, last)}, followed by \cpp{[first, middle)}, which mirrors the behaviour of the \cpp{std::rotate} algorithm. diff --git a/chapters/03_algorithms_14_uninitialized.tex b/chapters/03_algorithms_14_uninitialized.tex index 8a78051..f517069 100644 --- a/chapters/03_algorithms_14_uninitialized.tex +++ b/chapters/03_algorithms_14_uninitialized.tex @@ -7,6 +7,8 @@ \section{Uninitialized memory algorithms} The counted variants of the algorithms are not listed in this section\footnote{The names of these algorithms are particularly long and obnoxious.}. However, note that all algorithms in this section that operate on ranges have a counted variant, where the range is specified using an iterator and number of elements. These variants are used in some of the examples to demonstrate. \subsection{\texorpdfstring{\cpp{std::construct_at}, \cpp{std::destroy_at}}{\texttt{std::construct\_at}, \texttt{std::destroy\_at}}} +\index{\cpp{std::construct_at}} +\index{\cpp{std::destroy_at}} The \cpp{std::construct_at} and \cpp{std::destroy_at} algorithms will construct/destroy a single element at a given address. If additional arguments are specified, \cpp{std::construct_at} will forward these to the objects’ constructor. @@ -20,6 +22,10 @@ \subsection{\texorpdfstring{\cpp{std::construct_at}, \cpp{std::destroy_at}}{\tex \end{box-note} \subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\cpp{std::uninitialized_value_construct},\newline\cpp{std::uninitialized_fill}, \newline\cpp{std::destroy}}{\texttt{std::uninitialized\_default\_construct},\newline\texttt{std::uninitialized_value_construct},\newline\texttt{std::uninitialized_fill}, \newline\texttt{std::destroy}}} +\index{\cpp{std::uninitialized_default_construct}} +\index{\cpp{std::uninitialized_value_construct}} +\index{\cpp{std::uninitialized_fill}} +\index{\cpp{std::destroy}} The three uninitialized algorithms cover the default initialization, value initialization and copy initialization of elements. The \cpp{std::destroy} algorithm provides the destruction of elements without deallocating the underlying memory. @@ -38,6 +44,8 @@ \subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\ \end{box-note} \subsection{\texorpdfstring{\cpp{std::uninitialized_copy}, \cpp{std::uninitalized_move}}{\texttt{std::uninitialized\_copy}, \texttt{std::uninitalized\_move}}} +\index{\cpp{std::uninitialized_copy}} +\index{\cpp{std::uninitialized_move}} The \cpp{std::unitialized_copy} and \cpp{std::unitialized_move} algorithms follow the behaviour of \cpp{std::copy} and \cpp{std::move} algorithms with the distinction that the destination range is uninitialized memory. diff --git a/chapters/03_algorithms_15_heap.tex b/chapters/03_algorithms_15_heap.tex index f420d62..cf690a1 100644 --- a/chapters/03_algorithms_15_heap.tex +++ b/chapters/03_algorithms_15_heap.tex @@ -4,6 +4,9 @@ \section{Heap data structure} However, when using \cpp{std::priority_queue}, we lose access to the underlying data, which might be inconvenient. \subsection{\texorpdfstring{\cpp{std::make_heap}, \cpp{std::push_heap}, \cpp{std::pop_heap}}{\texttt{std::make\_heap}, \texttt{std::push\_heap}, \texttt{std::pop\_heap}}} +\index{\cpp{std::make_heap}} +\index{\cpp{std::push_heap}} +\index{\cpp{std::pop_heap}} A Heap data structure is a binary tree where each element satisfies the heap property: the value at the parent is greater or equal to the value of its children. @@ -34,6 +37,7 @@ \subsection{\texorpdfstring{\cpp{std::make_heap}, \cpp{std::push_heap}, \cpp{std \end{box-note} \subsection{\texorpdfstring{\cpp{std::sort_heap}}{\texttt{std::sort\_heap}}} +\index{\cpp{std::sort_heap}} The \cpp{std::sort_heap} will reorder the elements in a heap into a sorted order. Note that this is the same as repeatedly calling \cpp{std::pop_heap}; hence the algorithm has $O(n*logn)$ complexity. @@ -47,6 +51,8 @@ \subsection{\texorpdfstring{\cpp{std::sort_heap}}{\texttt{std::sort\_heap}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::is_heap}, \cpp{std::is_heap_until}}{\texttt{std::is\_heap}, \texttt{std::is\_heap\_until}}} +\index{\cpp{std::is_heap}} +\index{\cpp{std::is_heap_until}} The \cpp{std::is_heap} and \cpp{std::is_heap_until} algorithms check the heap invariant. @@ -64,6 +70,7 @@ \subsection{\texorpdfstring{\cpp{std::is_heap}, \cpp{std::is_heap_until}}{\textt \end{box-note} \subsection{Comparison with \texorpdfstring{\cpp{std::priority_queue}}{\texttt{std::priority\_queue}}} +\index{\cpp{std::priority_queue}} As mentioned at the beginning of this section, the heap algorithms provide effectively the same functionality as \cpp{std::priority_queue}. To demonstrate the differences, let's look at two versions of a topk algorithm: an algorithm that takes a range and returns the top k elements in sorted order as a \cpp{std::vector}. diff --git a/chapters/03_algorithms_16_searching.tex b/chapters/03_algorithms_16_searching.tex index f5c1011..4a385a7 100644 --- a/chapters/03_algorithms_16_searching.tex +++ b/chapters/03_algorithms_16_searching.tex @@ -3,6 +3,9 @@ \section{Search and compare algorithms} The search and compare category provides straightforward linear (when compared against a single value) and quadratic (when compared against a range) complexity algorithms. \subsection{\texorpdfstring{\cpp{std::find}, \cpp{std::find_if}, \cpp{std::find_if_not}}{\texttt{std::find}, \texttt{std::find\_if}, \texttt{std::find\_if\_not}}} +\index{\cpp{std::find}} +\index{\cpp{std::find_if}} +\index{\cpp{std::find_if_not}} The std::find algorithm provides a basic linear search. The standard provides three variants, one searching by value and two variants using a predicate. @@ -28,6 +31,7 @@ \subsection{\texorpdfstring{\cpp{std::find}, \cpp{std::find_if}, \cpp{std::find_ \end{box-note} \subsection{\texorpdfstring{\cpp{std::adjacent_find}}{\texttt{std::adjacent\_find}}} +\index{\cpp{std::adjacent_find}} The \cpp{std::adjacent_find} is a binary find algorithm that searches for pairs of adjacent elements in a single range. @@ -43,6 +47,7 @@ \subsection{\texorpdfstring{\cpp{std::adjacent_find}}{\texttt{std::adjacent\_fin \end{box-note} \subsection{\texorpdfstring{\cpp{std::search_n}}{\texttt{std::search\_n}}} +\index{\cpp{std::search_n}} The \cpp{std::search_n} algorithm searches for n instances of the given value. @@ -60,6 +65,7 @@ \subsection{\texorpdfstring{\cpp{std::search_n}}{\texttt{std::search\_n}}} Note that \cpp{std::search_n} is one exception to the \cpp{_n} naming scheme. \subsection{\texorpdfstring{\cpp{std::find_first_of}}{\texttt{std::find\_first\_of}}} +\index{\cpp{std::find_first_of}} Using \cpp{std::find_if}, we can easily search for a category of elements. However, sometimes it is more convenient to list the elements we are looking for exhaustively. @@ -75,6 +81,8 @@ \subsection{\texorpdfstring{\cpp{std::find_first_of}}{\texttt{std::find\_first\_ \end{box-note} \subsection{\texorpdfstring{\cpp{std::search}, \cpp{std::find_end}}{\texttt{std::search}, \texttt{std::find\_end}}} +\index{\cpp{std::search}} +\index{\cpp{std::find_end}} Both \cpp{std::search} and \cpp{std::find_end} algorithms search for a sub-sequence in a sequence. The \cpp{std::search} algorithm will return the first instance, and \cpp{std::find_end} will return the last. @@ -96,9 +104,14 @@ \subsubsection{Searchers} \footnotesize Example of using \cpp{std::search} with custom searchers. \tcblower \cppfile{code_examples/algorithms/searchers_code.h} +\index{\cpp{std::default_searcher}} +\index{\cpp{std::boyer_moore_searcher}} +\index{\cpp{std::boyer_moore_horspool_searcher}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::count}, \cpp{std::count_if}}{\texttt{std::count}, \texttt{std::count\_if}}} +\index{\cpp{std::count}} +\index{\cpp{std::count_if}} The \cpp{std::count} and \cpp{std::count_if} algorithms count the number of matching elements. @@ -114,6 +127,8 @@ \subsection{\texorpdfstring{\cpp{std::count}, \cpp{std::count_if}}{\texttt{std:: \end{box-note} \subsection{\texorpdfstring{\cpp{std::equal}, \cpp{std::mismatch}}{\texttt{std::equal}, \texttt{std::mismatch}}} +\index{\cpp{std::equal}} +\index{\cpp{std::mismatch}} The \cpp{std::equal} algorithm provides an equality comparison for ranges. diff --git a/chapters/03_algorithms_17_minmax.tex b/chapters/03_algorithms_17_minmax.tex index bc90437..fc6bcb9 100644 --- a/chapters/03_algorithms_17_minmax.tex +++ b/chapters/03_algorithms_17_minmax.tex @@ -23,6 +23,9 @@ \section{Min-Max algorithms} Please remember that when using casts like \cpp{const_cast}, you effectively override the compiler's judgment. Therefore it is entirely up to you to ensure that the given cast is valid. \subsection{\texorpdfstring{\cpp{std::min}, \cpp{std::max}, \cpp{std::minmax}}{\texttt{std::min}, \texttt{std::max}, \texttt{std::minmax}}} +\index{\cpp{std::min}} +\index{\cpp{std::max}} +\index{\cpp{std::minmax}} The basic versions of \cpp{std::min}, \cpp{std::max} and \cpp{std::minmax} operate on two elements, accepting their arguments by const-reference and returning by const-reference. Unfortunately, as mentioned earlier, this creates a constness problem, and we also must be careful to capture the result by value when passing in temporary objects. @@ -54,6 +57,7 @@ \subsection{\texorpdfstring{\cpp{std::min}, \cpp{std::max}, \cpp{std::minmax}}{\ \end{box-note} \subsection{\texorpdfstring{\cpp{std::clamp}}{\texttt{std::clamp}}} +\index{\cpp{std::clamp}} The \cpp{std::clamp} algorithm takes three arguments, the value, the minimum and the maximum bound and will clamp the value between the provided minimum and maximum: @@ -73,8 +77,10 @@ \subsection{\texorpdfstring{\cpp{std::clamp}}{\texttt{std::clamp}}} \cppfile{code_examples/algorithms/clamp_code.h} \end{box-note} - \subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \cpp{std::minmax_element}}{\texttt{std::min\_element}, \texttt{std::max\_element}, \texttt{std::minmax\_element}}} +\index{\cpp{std::min_element}} +\index{\cpp{std::max_element}} +\index{\cpp{std::minmax_element}} The element versions of min-max algorithms operate on ranges and, instead of returning by const-reference or value, return an iterator to the minimum or maximum elements. diff --git a/chapters/06_cpp20_ranges_in_depth.tex b/chapters/06_cpp20_ranges_in_depth.tex index 2ff1fba..77f322c 100644 --- a/chapters/06_cpp20_ranges_in_depth.tex +++ b/chapters/06_cpp20_ranges_in_depth.tex @@ -1,4 +1,4 @@ -\chapter{\CC20 Ranges} +\chapter{Introduction to \CC20 Ranges} C++20 Ranges, also known as STL v2, effectively replaces existing STL algorithms and facilities. In this article, I will guide you through the changes that Ranges introduce, talk about Views, which are a new composable approach to algorithms and show examples of FizzBuzz using three different methods, all utilizing some aspects of Ranges. diff --git a/chapters/XX_glossary.tex b/chapters/XX_glossary.tex deleted file mode 100644 index 2e28836..0000000 --- a/chapters/XX_glossary.tex +++ /dev/null @@ -1,2 +0,0 @@ -\clearpage -\printglossary \ No newline at end of file From 8e4cc8e69a5c4c0de3e07c80045d3757f334326b Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 12 Jul 2022 12:19:04 +0200 Subject: [PATCH 02/96] Version 0.1.1 --- book.tex | 5 +++-- chapters/03_algorithms_05_divide.tex | 2 +- .../{06_cpp20_ranges_in_depth.tex => 04_ranges_in_depth.tex} | 2 +- chapters/{04_TODO.tex => Y4_TODO.tex} | 0 chapters/{04_making_your_own.tex => Y4_making_your_own.tex} | 0 ...05_parallel_algorithms.tex => Y5_parallel_algorithms.tex} | 0 chapters/{07_cpp20_views.tex => Y7_cpp20_views.tex} | 0 chapters/{08_extra_topics.tex => Y8_extra_topics.tex} | 0 8 files changed, 5 insertions(+), 4 deletions(-) rename chapters/{06_cpp20_ranges_in_depth.tex => 04_ranges_in_depth.tex} (99%) rename chapters/{04_TODO.tex => Y4_TODO.tex} (100%) rename chapters/{04_making_your_own.tex => Y4_making_your_own.tex} (100%) rename chapters/{05_parallel_algorithms.tex => Y5_parallel_algorithms.tex} (100%) rename chapters/{07_cpp20_views.tex => Y7_cpp20_views.tex} (100%) rename chapters/{08_extra_topics.tex => Y8_extra_topics.tex} (100%) diff --git a/book.tex b/book.tex index 7f70bb9..ccfb9d3 100644 --- a/book.tex +++ b/book.tex @@ -4,7 +4,7 @@ %\usepackage{showframe} %\checkandfixthelayout -\newcommand{\version}{0.1.0} +\newcommand{\version}{0.1.1} \usepackage{hyperref} \hypersetup{ colorlinks=true, @@ -40,6 +40,7 @@ \input{chapters/02_introduction} % In review \input{chapters/03_algorithms_00_main} % In review +%\input{chapters/04_ranges_in_depth} % TODO % Ranges % Views @@ -51,7 +52,7 @@ %\input{chapters/04_TODO} % TODO %\input{chapters/04_making_your_own} % TODO %\input{chapters/05_parallel_algorithms} % TODO -%\input{chapters/06_cpp20_ranges_in_depth}% TODO + %\input{chapters/07_cpp20_views}% TODO \appendix diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index b560bc6..911a951 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -87,7 +87,7 @@ \subsection{\texorpdfstring{\cpp{std::binary_search}}{\texttt{std::binary\_searc \end{box-note} \subsection{\texorpdfstring{\cpp{bsearch} - C standard library}{\texttt{bsearch} - C standard library}} -\index{\cpp{std::bsearch}} +\index{\cpp{bsearch}} From the C standard library, C++ inherits \cpp{bsearch}. This algorithm returns one of the elements equal to the provided key, or \cpp{nullptr} if none such element is found. diff --git a/chapters/06_cpp20_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex similarity index 99% rename from chapters/06_cpp20_ranges_in_depth.tex rename to chapters/04_ranges_in_depth.tex index 77f322c..a1f3eaa 100644 --- a/chapters/06_cpp20_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -1,4 +1,4 @@ -\chapter{Introduction to \CC20 Ranges} +\chapter{Introduction to Ranges} C++20 Ranges, also known as STL v2, effectively replaces existing STL algorithms and facilities. In this article, I will guide you through the changes that Ranges introduce, talk about Views, which are a new composable approach to algorithms and show examples of FizzBuzz using three different methods, all utilizing some aspects of Ranges. diff --git a/chapters/04_TODO.tex b/chapters/Y4_TODO.tex similarity index 100% rename from chapters/04_TODO.tex rename to chapters/Y4_TODO.tex diff --git a/chapters/04_making_your_own.tex b/chapters/Y4_making_your_own.tex similarity index 100% rename from chapters/04_making_your_own.tex rename to chapters/Y4_making_your_own.tex diff --git a/chapters/05_parallel_algorithms.tex b/chapters/Y5_parallel_algorithms.tex similarity index 100% rename from chapters/05_parallel_algorithms.tex rename to chapters/Y5_parallel_algorithms.tex diff --git a/chapters/07_cpp20_views.tex b/chapters/Y7_cpp20_views.tex similarity index 100% rename from chapters/07_cpp20_views.tex rename to chapters/Y7_cpp20_views.tex diff --git a/chapters/08_extra_topics.tex b/chapters/Y8_extra_topics.tex similarity index 100% rename from chapters/08_extra_topics.tex rename to chapters/Y8_extra_topics.tex From c137f01aa9e32142fcacde0a4ae5e1a8d8d53f70 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 12 Jul 2022 12:49:06 +0200 Subject: [PATCH 03/96] Update Readme and cover page. --- README.md | 12 ++++++++++-- static/book_cover.png | Bin 0 -> 73473 bytes 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 static/book_cover.png diff --git a/README.md b/README.md index e3a9f50..605c987 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,10 @@ -# book-cpp-algorithms -The Standard Algorithms in C++. +# Book: A Complete Guide to Standard C++ Algorithms + +This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". + +![Book Cover](static/book_cover.png) + +## Changelog + +- `0.1.1` Added index and a cover page, small text changes. +- `0.1.0` First pre-release \ No newline at end of file diff --git a/static/book_cover.png b/static/book_cover.png new file mode 100644 index 0000000000000000000000000000000000000000..e6a60a6d77722a9e93158af76eec65512479f92c GIT binary patch literal 73473 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 Date: Thu, 21 Jul 2022 23:48:44 +0200 Subject: [PATCH 04/96] Update README.md Link to latest release. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 605c987..90556a4 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,11 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -![Book Cover](static/book_cover.png) +[Latest PDF release (v0.1.1)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.1.1/book_with_cover_v0.1.1.pdf) + +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.1.1/book_with_cover_v0.1.1.pdf) ## Changelog - `0.1.1` Added index and a cover page, small text changes. -- `0.1.0` First pre-release \ No newline at end of file +- `0.1.0` First pre-release From 64319ac3afa909937c1f5318bb2ba63c4ee44bd8 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 22 Jul 2022 10:02:29 +0200 Subject: [PATCH 05/96] Example for using concepts in user code. --- code_examples/CMakeLists.txt | 1 + code_examples/ranges/CMakeLists.txt | 2 ++ code_examples/ranges/concepts.cpp | 18 ++++++++++++++++++ code_examples/ranges/concepts_code.h | 8 ++++++++ 4 files changed, 29 insertions(+) create mode 100644 code_examples/ranges/CMakeLists.txt create mode 100644 code_examples/ranges/concepts.cpp create mode 100644 code_examples/ranges/concepts_code.h diff --git a/code_examples/CMakeLists.txt b/code_examples/CMakeLists.txt index 85dfbdf..36f5e92 100644 --- a/code_examples/CMakeLists.txt +++ b/code_examples/CMakeLists.txt @@ -13,3 +13,4 @@ add_subdirectory(introduction) add_subdirectory(algorithms) add_subdirectory(theory) add_subdirectory(extras) +add_subdirectory(ranges) diff --git a/code_examples/ranges/CMakeLists.txt b/code_examples/ranges/CMakeLists.txt new file mode 100644 index 0000000..06aacc0 --- /dev/null +++ b/code_examples/ranges/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(concepts concepts.cpp) +install(TARGETS concepts DESTINATION bin) diff --git a/code_examples/ranges/concepts.cpp b/code_examples/ranges/concepts.cpp new file mode 100644 index 0000000..640196a --- /dev/null +++ b/code_examples/ranges/concepts.cpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include +#include + +#include "concepts_code.h" + +int main() { + std::vector data{1, 2, 3, 4, 5, 6, 7, 8}; + std::vector out; + my_function(data, std::back_inserter(out)); + assert(out.size() == 2); + assert(out[0] == 5); + assert(out[1] == 7); + std::cerr << "."; +} diff --git a/code_examples/ranges/concepts_code.h b/code_examples/ranges/concepts_code.h new file mode 100644 index 0000000..daa02f7 --- /dev/null +++ b/code_examples/ranges/concepts_code.h @@ -0,0 +1,8 @@ +template +auto my_function(T&& rng, + std::output_iterator> auto it) { + if (rng.size() >= 5) + *it++ = rng[4]; + if (rng.size() >= 7) + *it++ = rng[6]; +} From 247b45c7cda3b0de01e5d7bd3f8293437748e264 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 22 Jul 2022 10:03:22 +0200 Subject: [PATCH 06/96] Updates from Overleaf --- book.tex | 10 +++++----- chapters/04_ranges_in_depth.tex | 26 ++++++++++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/book.tex b/book.tex index ccfb9d3..631b2ff 100644 --- a/book.tex +++ b/book.tex @@ -31,16 +31,16 @@ \begin{document} \frontmatter -\input{chapters/00_title_page} +%\input{chapters/00_title_page} -\input{chapters/01_preface} % In review +%\input{chapters/01_preface} % In review \mainmatter \tableofcontents -\input{chapters/02_introduction} % In review -\input{chapters/03_algorithms_00_main} % In review -%\input{chapters/04_ranges_in_depth} % TODO +%\input{chapters/02_introduction} % In review +%\input{chapters/03_algorithms_00_main} % In review +\input{chapters/04_ranges_in_depth} % TODO % Ranges % Views diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index a1f3eaa..71d3d77 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -1,16 +1,30 @@ \chapter{Introduction to Ranges} -C++20 Ranges, also known as STL v2, effectively replaces existing STL algorithms and facilities. In this article, I will guide you through the changes that Ranges introduce, talk about Views, which are a new composable approach to algorithms and show examples of FizzBuzz using three different methods, all utilizing some aspects of Ranges. +The \CC20 standard introduced the Ranges library (a.k.a. STLv2), which effectively replaces existing algorithms and facilities. +In this chapter, we will go over the main changes. -Note, however, that Ranges are one of the features that landed in C++20 in a half-baked state. C++23 should bring us much closer to comprehensive support. Some of the examples will therefore use the range v3 library. +Note that Ranges are one of the features that landed in \CC20 in a partial state. +Despite \CC23 introducing plenty of additional features, we already know that many features will still not make it into \CC23. -\section{Ranges vs old STL} +\section{Reliance on concepts} -As already mentioned, ranges are a drop-in replacement for STL. However, they introduce both internal and user-facing changes that overall improve their usefulness. +The standard has always described what is required of each argument passed to an algorithm. However, this description was purely informative, and the language did not offer first-class tools to enforce these requirements. Because of this, the error messages were often confusing. -\subsection{Concepts} +Concepts are a new C++20 language feature that permits library implementors to constrain the arguments of generic code. Concepts are beyond the scope of this book; however, there are two consequences worth noting. -Ranges rely on concepts to specify what types of parameters can participate in each overload. Therefore, making a mistake when using ranges will lead to shorter and more to the point error messages. +First, the definitions of all concepts are now part of the standard library, e.g. you can look up what exactly it means for a type to be a \cpp{random_access_range} and also use these concepts to constrain your code. + +// example + +Second, error messages now reference unsatisfied constraints. + +// example + + + + + +\section{TODO} A typical example is trying to sort a std::list. Unfortunately, this is an easy mistake to make if you are new to C++. From 95496cdc2f4ab64965d0175d164cf01c00f91192 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 22 Jul 2022 13:04:25 +0200 Subject: [PATCH 07/96] Example of infinite range. --- code_examples/ranges/CMakeLists.txt | 3 +++ code_examples/ranges/infinite.cpp | 12 ++++++++++++ code_examples/ranges/infinite_code.h | 7 +++++++ 3 files changed, 22 insertions(+) create mode 100644 code_examples/ranges/infinite.cpp create mode 100644 code_examples/ranges/infinite_code.h diff --git a/code_examples/ranges/CMakeLists.txt b/code_examples/ranges/CMakeLists.txt index 06aacc0..babfcdc 100644 --- a/code_examples/ranges/CMakeLists.txt +++ b/code_examples/ranges/CMakeLists.txt @@ -1,2 +1,5 @@ add_executable(concepts concepts.cpp) install(TARGETS concepts DESTINATION bin) + +add_executable(infinite infinite.cpp) +# Compile-only diff --git a/code_examples/ranges/infinite.cpp b/code_examples/ranges/infinite.cpp new file mode 100644 index 0000000..7378bd3 --- /dev/null +++ b/code_examples/ranges/infinite.cpp @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main() { +#include "infinite_code.h" +} diff --git a/code_examples/ranges/infinite_code.h b/code_examples/ranges/infinite_code.h new file mode 100644 index 0000000..b38d30e --- /dev/null +++ b/code_examples/ranges/infinite_code.h @@ -0,0 +1,7 @@ +std::vector dt = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; +std::ranges::shuffle(dt, std::mt19937(std::random_device()())); + +auto pos = std::ranges::find( + dt.begin(), + std::unreachable_sentinel, + 7); From 2218605d905cdf92955ce71229694bf2d0434ec0 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 22 Jul 2022 13:04:55 +0200 Subject: [PATCH 08/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 46 ++++++++++++++------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 71d3d77..c50cb02 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -14,46 +14,40 @@ \section{Reliance on concepts} First, the definitions of all concepts are now part of the standard library, e.g. you can look up what exactly it means for a type to be a \cpp{random_access_range} and also use these concepts to constrain your code. -// example +\begin{box-note} +\footnotesize Example of using standard concepts in user code. The function accepts any random access range as the first argument and an output iterator with the same underlying type as the second argument. +\tcblower +\cppfile{code_examples/ranges/concepts_code.h} +\end{box-note} -Second, error messages now reference unsatisfied constraints. +Second, error messages now reference unsatisfied constraints instead of reporting an error deep in the library implementation. -// example +\begin{minted}[fontsize=\footnotesize]{cpp} +note: candidate: 'template + requires (random_access_iterator<_Iter>) +\end{minted} +\section{Notion of a Range} +Non-range algorithms have always operated strictly using iterators. Conceptually, the range of elements has been defined by two iterators \cpp{[first, last)} or when working with all elements from a standard data structure \cpp{[begin, end)}. +Range algorithms formally define a range as an iterator and a sentinel pair. Moving the sentinel to a different type unlocks the potential to simplify code where an end iterator doesn't naturally map to an element. The standard offers two default sentinel types, \cpp{std::default_sentinel} and \cpp{std::unreachable_sentinel}. +Apart from simplifying certain use cases, sentinels also allow for infinite ranges and potential performance improvements. -\section{TODO} -A typical example is trying to sort a std::list. Unfortunately, this is an easy mistake to make if you are new to C++. -\begin{box-note} -\begin{cppcode} -#include -#include -#include -#include -int main() { - std::list dt = {1, 4, 2, 3}; - std::ranges::sort(dt.begin(), dt.end()); - std::ranges::copy(dt.begin(), dt.end(), - std::ostream_iterator(std::cout, ",")); -} -\end{cppcode} -\end{box-note} +And finally, with the introduction of the range, algorithms now provide range overloads, leading to more concise and easier-to-read code. + + -Instead of receiving a confusing error about the minus operator, we now get the exact problem as the first error: +\section{TODO} -\begin{verbatim} -include/c++/12.0.0/bits/ranges_algo.h:1810:14: note: because 'std::_List_iterator' does not satisfy 'random_access_iterator' -\end{verbatim} -We can inspect the concepts defined by the Ranges library, as these are part of the standard. For example, the concept of a range is very straightforward, and it simply mandates that the expressions std::ranges::begin(rng) and std::ranges::end(rng) are valid. If you want to read up on concepts, check out my concepts guide. The fundamental change here is that end() no longer needs to return the same type as begin(). The returned sentinel only needs to be comparable to the iterator type returned by begin(). -Apart from simplifying certain use cases, it also allows for infinite ranges and potential performance improvement. + \begin{box-note} \begin{cppcode} @@ -71,7 +65,7 @@ \section{TODO} We can only use this trick when we have a contextual guarantee that the algorithm will terminate without going out of bounds, but it brings algorithms on par with hand-written code. -And finally, with the introduction of the range concept, we can also save up on writing and use the range accepting variants of algorithms. + \begin{box-note} \begin{cppcode} From 9fc9cb2d21f358e6b3f91477f990a86847337f3f Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 22 Jul 2022 13:21:17 +0200 Subject: [PATCH 09/96] Example of rangified sort. --- code_examples/ranges/CMakeLists.txt | 3 +++ code_examples/ranges/rangified.cpp | 14 ++++++++++++++ code_examples/ranges/rangified_code.h | 2 ++ 3 files changed, 19 insertions(+) create mode 100644 code_examples/ranges/rangified.cpp create mode 100644 code_examples/ranges/rangified_code.h diff --git a/code_examples/ranges/CMakeLists.txt b/code_examples/ranges/CMakeLists.txt index babfcdc..10f4248 100644 --- a/code_examples/ranges/CMakeLists.txt +++ b/code_examples/ranges/CMakeLists.txt @@ -3,3 +3,6 @@ install(TARGETS concepts DESTINATION bin) add_executable(infinite infinite.cpp) # Compile-only + +add_executable(rangified rangified.cpp) +install(TARGETS rangified DESTINATION bin) diff --git a/code_examples/ranges/rangified.cpp b/code_examples/ranges/rangified.cpp new file mode 100644 index 0000000..5267aa5 --- /dev/null +++ b/code_examples/ranges/rangified.cpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main() { +#include "rangified_code.h" +auto cmp = {1, 2, 3, 4}; +assert(std::ranges::equal(cmp, dt)); +} diff --git a/code_examples/ranges/rangified_code.h b/code_examples/ranges/rangified_code.h new file mode 100644 index 0000000..e0841ab --- /dev/null +++ b/code_examples/ranges/rangified_code.h @@ -0,0 +1,2 @@ +std::vector dt = {1, 4, 2, 3}; +std::ranges::sort(dt); From 0dcaa9c3980595119da0973ef505c3e5a46b2065 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 22 Jul 2022 13:21:49 +0200 Subject: [PATCH 10/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index c50cb02..f4264cd 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -35,37 +35,21 @@ \section{Notion of a Range} Apart from simplifying certain use cases, sentinels also allow for infinite ranges and potential performance improvements. +\begin{box-note} +\footnotesize Example of an infinite range when the data guarantees termination. Using \cpp{std::unreachable_sentinel} causes the boundary check to be optimized-out, removing one comparison from the loop. +\tcblower +\cppfile{code_examples/ranges/infinite_code.h} +\end{box-note} +We can only use this approach when we have a contextual guarantee that the algorithm will terminate without going out of bounds. However, this removes one of the few instances when algorithms couldn't perform as well as handwritten code. And finally, with the introduction of the range, algorithms now provide range overloads, leading to more concise and easier-to-read code. -\section{TODO} - - - -The fundamental change here is that end() no longer needs to return the same type as begin(). The returned sentinel only needs to be comparable to the iterator type returned by begin(). - - - -\begin{box-note} -\begin{cppcode} -std::vector dt = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; -std::ranges::shuffle(dt, std::mt19937(std::random_device()())); -auto pos = std::ranges::find(dt.begin(), - std::unreachable_sentinel, - 7); -std::ranges::copy(dt.begin(), ++pos, - std::ostream_iterator(std::cout, ",")); -\end{cppcode} -\end{box-note} - -The std::unreachable\_sentinel always returns false when compared to an iterator. The compiler will therefore optimize out the boundary check it != end since this expression is then always true. - -We can only use this trick when we have a contextual guarantee that the algorithm will terminate without going out of bounds, but it brings algorithms on par with hand-written code. +\section{TODO} \begin{box-note} \begin{cppcode} From 7808904656f757c253e18216a5da239a4ffa8fd7 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 25 Jul 2022 10:08:56 +0200 Subject: [PATCH 11/96] Example for projection. --- code_examples/ranges/CMakeLists.txt | 3 +++ code_examples/ranges/projection_code.h | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 code_examples/ranges/projection_code.h diff --git a/code_examples/ranges/CMakeLists.txt b/code_examples/ranges/CMakeLists.txt index 10f4248..b8b89f1 100644 --- a/code_examples/ranges/CMakeLists.txt +++ b/code_examples/ranges/CMakeLists.txt @@ -6,3 +6,6 @@ add_executable(infinite infinite.cpp) add_executable(rangified rangified.cpp) install(TARGETS rangified DESTINATION bin) + +# Not-checked +# projection_code.h diff --git a/code_examples/ranges/projection_code.h b/code_examples/ranges/projection_code.h new file mode 100644 index 0000000..798eb3d --- /dev/null +++ b/code_examples/ranges/projection_code.h @@ -0,0 +1,8 @@ +struct Account{ + double value(); +}; + + +std::vector data = get_data(); + +std::ranges::sort(data, std::greater<>{}, &Account::value); From 18444119d22566f446d96532240f98fbd7a4448d Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 25 Jul 2022 10:12:22 +0200 Subject: [PATCH 12/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index f4264cd..eb3212b 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -45,6 +45,18 @@ \section{Notion of a Range} And finally, with the introduction of the range, algorithms now provide range overloads, leading to more concise and easier-to-read code. +\begin{box-note} +\footnotesize Example of using the range overload of \cpp{std::sort}. +\tcblower +\cppfile{code_examples/ranges/rangified_code.h} +\end{box-note} + +\section{Projections} + +Each range algorithm comes with an additional argument, the projection. +The projection is effectively a baked-in transform operation applied before an element is passed into the main functor. + +The projection can be any invocable, which includes member pointers. From 074393b1f59601465bf6bb376f96e58f14699fdd Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 25 Jul 2022 10:32:34 +0200 Subject: [PATCH 13/96] Example of projection effects on resulting type. --- code_examples/ranges/CMakeLists.txt | 3 +++ code_examples/ranges/projected_type.cpp | 10 ++++++++++ code_examples/ranges/projected_type_code.h | 15 +++++++++++++++ code_examples/ranges/projection_code.h | 1 - 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 code_examples/ranges/projected_type.cpp create mode 100644 code_examples/ranges/projected_type_code.h diff --git a/code_examples/ranges/CMakeLists.txt b/code_examples/ranges/CMakeLists.txt index b8b89f1..6c4d6b9 100644 --- a/code_examples/ranges/CMakeLists.txt +++ b/code_examples/ranges/CMakeLists.txt @@ -9,3 +9,6 @@ install(TARGETS rangified DESTINATION bin) # Not-checked # projection_code.h + +add_executable(projected_type projected_type.cpp) +install(TARGETS projected_type DESTINATION bin) diff --git a/code_examples/ranges/projected_type.cpp b/code_examples/ranges/projected_type.cpp new file mode 100644 index 0000000..76ef4a4 --- /dev/null +++ b/code_examples/ranges/projected_type.cpp @@ -0,0 +1,10 @@ +#include +#include +#include +#include +#include + +int main() { +#include "projected_type_code.h" +std::cerr << "."; +} diff --git a/code_examples/ranges/projected_type_code.h b/code_examples/ranges/projected_type_code.h new file mode 100644 index 0000000..b74b879 --- /dev/null +++ b/code_examples/ranges/projected_type_code.h @@ -0,0 +1,15 @@ +struct A{}; +struct B{}; + +std::vector data(5); + +std::vector out1; +// std::vector out1; would not compile +std::ranges::copy_if(data, std::back_inserter(out1), + [](B) { return true; }, // predicate accepts B + [](A) { return B{}; }); // projection projects A->B for the predicate only + +std::vector out2; +std::ranges::transform(data, std::back_inserter(out2), + [](auto x) { return x; }, // no-op transformation functor + [](A) { return B{}; }); // projection projects A->B for the functor only diff --git a/code_examples/ranges/projection_code.h b/code_examples/ranges/projection_code.h index 798eb3d..f7b5285 100644 --- a/code_examples/ranges/projection_code.h +++ b/code_examples/ranges/projection_code.h @@ -2,7 +2,6 @@ struct Account{ double value(); }; - std::vector data = get_data(); std::ranges::sort(data, std::greater<>{}, &Account::value); From fb589951c2dce99df52a0dfed7f2945b39f752bc Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 25 Jul 2022 10:35:14 +0200 Subject: [PATCH 14/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index eb3212b..f5fe134 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -58,24 +58,28 @@ \section{Projections} The projection can be any invocable, which includes member pointers. +\begin{box-note} +\footnotesize Example of using a projection to sort elements of a range based on a computed value from an element method. +\tcblower +\cppfile{code_examples/ranges/projection_code.h} +\end{box-note} - - -\section{TODO} +Because the projection result is only used for the main functor, it does not modify the output of the algorithm except for \cpp{std::ranges::transform}. \begin{box-note} -\begin{cppcode} -std::vector dt = {1, 4, 2, 3}; -std::ranges::sort(dt); -\end{cppcode} +\footnotesize Example of the difference between \cpp{std::ranges::copy_if}, which uses the projection result for the predicate and \cpp{std::ranges::transform}, which uses the projection result as the input for the transformation function (therefore making it affect the output type). +\tcblower +\cppfile{code_examples/ranges/projection_type_code.h} \end{box-note} + \subsection{Projections} A massive new feature that, on the surface, seems trivial is the support for projections. A projection is a unary invocable that is applied to every element. This often completely removes the need to write complex lambdas, and when it doesn’t, it simplifies them significantly. An invocable is an extension of callable and also accepts member pointers. + \begin{box-note} \begin{cppcode} struct Account { From a644cc57cf8bfef09bf1b226f6be52b2af350179 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 26 Jul 2022 10:21:21 +0200 Subject: [PATCH 15/96] Example for dangling protection. --- code_examples/ranges/CMakeLists.txt | 3 +++ code_examples/ranges/dangling.cpp | 7 +++++++ code_examples/ranges/dangling_code.h | 11 +++++++++++ 3 files changed, 21 insertions(+) create mode 100644 code_examples/ranges/dangling.cpp create mode 100644 code_examples/ranges/dangling_code.h diff --git a/code_examples/ranges/CMakeLists.txt b/code_examples/ranges/CMakeLists.txt index 6c4d6b9..0e07ab4 100644 --- a/code_examples/ranges/CMakeLists.txt +++ b/code_examples/ranges/CMakeLists.txt @@ -12,3 +12,6 @@ install(TARGETS rangified DESTINATION bin) add_executable(projected_type projected_type.cpp) install(TARGETS projected_type DESTINATION bin) + +add_executable(dangling dangling.cpp) +install(TARGETS dangling DESTINATION bin) diff --git a/code_examples/ranges/dangling.cpp b/code_examples/ranges/dangling.cpp new file mode 100644 index 0000000..a6d8c5b --- /dev/null +++ b/code_examples/ranges/dangling.cpp @@ -0,0 +1,7 @@ +#include +#include + +int main() { +#include "dangling_code.h" +std::cerr << "."; +} diff --git a/code_examples/ranges/dangling_code.h b/code_examples/ranges/dangling_code.h new file mode 100644 index 0000000..a651fe4 --- /dev/null +++ b/code_examples/ranges/dangling_code.h @@ -0,0 +1,11 @@ +const char* c_str = "1234567890"; + +// find on a temporary string_view +auto sep1 = std::ranges::find(std::string_view(c_str), '0'); +// OK, string_view is a borrowed range, *sep1 == '0', + +int bad = 1234567890; + +// find on a temporary string +auto sep2 = std::ranges::find(std::to_string(bad), '0'); +// decltype(sep2) == std::ranges::dangling, *sep2 would not compile From ad1cbf5addc82cd291e0a0913e9468f34049f543 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 26 Jul 2022 10:22:27 +0200 Subject: [PATCH 16/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 47 +++++++-------------------------- 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index f5fe134..9b7d208 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -69,54 +69,25 @@ \section{Projections} \begin{box-note} \footnotesize Example of the difference between \cpp{std::ranges::copy_if}, which uses the projection result for the predicate and \cpp{std::ranges::transform}, which uses the projection result as the input for the transformation function (therefore making it affect the output type). \tcblower -\cppfile{code_examples/ranges/projection_type_code.h} +\cppfile{code_examples/ranges/projected_type_code.h} \end{box-note} +\section{Dangling iterator protection} -\subsection{Projections} - -A massive new feature that, on the surface, seems trivial is the support for projections. A projection is a unary invocable that is applied to every element. +Because range versions of algorithms provide overloads that accept entire ranges, they open the potential for dangling iterators when users pass in a temporary range. -This often completely removes the need to write complex lambdas, and when it doesn’t, it simplifies them significantly. An invocable is an extension of callable and also accepts member pointers. +However, for some ranges, passing in a temporary is not an issue. To distinguish these two cases and to prevent dangling iterators, the range library has the concepts of a borrowed range and a special type \cpp{std::ranges::dangling}. +Borrowed ranges are ranges that "borrow" their elements from another range. Primary examples are \cpp{std::string_view} and \cpp{std::span}. For borrowed ranges, the lifetime of the elements is not tied to the lifetime of the range itself. \begin{box-note} -\begin{cppcode} -struct Account { - std::string owner; - double value(); - double base(); -}; -std::vector acc = get_accounts(); -// member -std::ranges::sort(acc,{},&Account::owner); -// member function -std::ranges::sort(acc,{},&Account::value); -// lambda -std::ranges::sort(acc,{},[](const auto& a) { - return a.value()+a.base(); -}); -\end{cppcode} +\footnotesize Example demonstrating the different handling for a borrowed range \cpp{std::string_view} and a \cpp{std::string}. +\tcblower +\cppfile{code_examples/ranges/dangling_code.h} \end{box-note} -Without projections, we would have to include this logic as part of a custom comparator. - -\begin{box-note} -\begin{cppcode} -std::vector dt = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; -std::vector result; -std::ranges::transform(dt, - dt | std::views::reverse, - std::back_inserter(result), - std::minus(), - [](int v) { return v*v; }, - [](int v) { return v*v; }); -std::ranges::copy(result, - std::ostream_iterator(std::cout, ",")); -\end{cppcode} -\end{box-note} +\subsection{Projections} -This is a slight foreshadowing for views, but I wanted to include another example that utilized two ranges as input. In such a case, we get two separate projections. Note that these projections can also return different return types, as long as they match up with the operation (here std::minus). \subsection{The small stuff} From 81f032ad93f2829172273a47dd543f4176f0f620 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 26 Jul 2022 10:24:11 +0200 Subject: [PATCH 17/96] Borrowed ranges optin. --- code_examples/ranges/borrowed_optin_code.h | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 code_examples/ranges/borrowed_optin_code.h diff --git a/code_examples/ranges/borrowed_optin_code.h b/code_examples/ranges/borrowed_optin_code.h new file mode 100644 index 0000000..f80215a --- /dev/null +++ b/code_examples/ranges/borrowed_optin_code.h @@ -0,0 +1,2 @@ +template +inline constexpr bool std::ranges::enable_borrowed_range> = true; From c88d2c5d91158ed01941fd8a816fcab6726641ae Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 26 Jul 2022 10:25:07 +0200 Subject: [PATCH 18/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 9b7d208..81141b6 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -86,29 +86,22 @@ \section{Dangling iterator protection} \cppfile{code_examples/ranges/dangling_code.h} \end{box-note} -\subsection{Projections} - - -\subsection{The small stuff} - -One last “small” feature I wanted to mention here is the prevention of dangling iterators. Mainly because even if you don’t care for it, you might find use-cases for this particular pattern in your codebase. +User types can declare themselves as borrowed ranges by specializing the \cpp{enable_borrowed_range} constant. \begin{box-note} -\begin{cppcode} -auto good = "1234567890"; -auto sep1 = std::ranges::find(std::string_view(good), '0'); -std::cout << *sep1 << "\n"; -auto bad = 1234567890; -auto sep2 = std::ranges::find(std::to_string(bad), '0'); -std::cout << *sep2 << "\n"; -\end{cppcode} +\footnotesize Example demonstrating declaring MyView as a borrowed range. +\tcblower +\cppfile{code_examples/ranges/borrowed_optin_code.h} \end{box-note} +To opt-in your ranges to work as temporaries, you need to specialize the enable\_borrowed\_range constant: + + You might recognize the problem here. If we weren’t using range variants of the algorithms, the “bad” variant would crash at runtime. However, with ranges, this code will not compile. When a range-based algorithm is invoked with a temporary range that owns its elements, the algorithm will return a special iterator std::ranges::dangling. Note that the first variant with a std::string\_view will still work just fine. String view is a type of range that doesn’t own its elements, and its iterators are freestanding (they don’t depend on the instance of string\_view), so it is perfectly valid to pass such temporary into a range-based algorithm. -To opt-in your ranges to work as temporaries, you need to specialize the enable\_borrowed\_range constant: + \begin{box-note} \begin{cppcode} From 594f03871ae828e46bf9d6054689fb54adef8aaf Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 09:50:21 +0200 Subject: [PATCH 19/96] View composition example. --- code_examples/ranges/CMakeLists.txt | 4 ++++ code_examples/ranges/view_demo.cpp | 7 +++++++ code_examples/ranges/view_demo_code.h | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 code_examples/ranges/view_demo.cpp create mode 100644 code_examples/ranges/view_demo_code.h diff --git a/code_examples/ranges/CMakeLists.txt b/code_examples/ranges/CMakeLists.txt index 0e07ab4..b1d8700 100644 --- a/code_examples/ranges/CMakeLists.txt +++ b/code_examples/ranges/CMakeLists.txt @@ -15,3 +15,7 @@ install(TARGETS projected_type DESTINATION bin) add_executable(dangling dangling.cpp) install(TARGETS dangling DESTINATION bin) + +# Does not compile with GCC 11 +#add_executable(view_demo view_demo.cpp) +# Compile-only diff --git a/code_examples/ranges/view_demo.cpp b/code_examples/ranges/view_demo.cpp new file mode 100644 index 0000000..ff29d01 --- /dev/null +++ b/code_examples/ranges/view_demo.cpp @@ -0,0 +1,7 @@ +#include +#include +#include + +int main() { +#include "view_demo_code.h" +} diff --git a/code_examples/ranges/view_demo_code.h b/code_examples/ranges/view_demo_code.h new file mode 100644 index 0000000..db36f88 --- /dev/null +++ b/code_examples/ranges/view_demo_code.h @@ -0,0 +1,5 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7, 8, 9}; +for (auto v : data | std::views::reverse | + std::views::take(3) | std::views::reverse) { + // iterate over 7, 8, 9 (in order) +} From dfe7f0c0d72f84c7a19e4bf5b2e9b1d566c16082 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 09:50:45 +0200 Subject: [PATCH 20/96] Updates from Overleaf --- book.tex | 15 +-- chapters/04_ranges_in_depth.tex | 230 ++------------------------------ chapters/Y7_cpp20_views.tex | 0 3 files changed, 13 insertions(+), 232 deletions(-) delete mode 100644 chapters/Y7_cpp20_views.tex diff --git a/book.tex b/book.tex index 631b2ff..5277fbd 100644 --- a/book.tex +++ b/book.tex @@ -40,20 +40,7 @@ %\input{chapters/02_introduction} % In review %\input{chapters/03_algorithms_00_main} % In review -\input{chapters/04_ranges_in_depth} % TODO - -% Ranges -% Views -% Iterator adapters -% Parallel algorithms -% Making your own (Algorithm, View, Container) -% Related topics ??? - -%\input{chapters/04_TODO} % TODO -%\input{chapters/04_making_your_own} % TODO -%\input{chapters/05_parallel_algorithms} % TODO - -%\input{chapters/07_cpp20_views}% TODO +\input{chapters/04_ranges_in_depth} % In review \appendix \input{chapters/XX_index} diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 81141b6..98ff7ea 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -94,230 +94,24 @@ \section{Dangling iterator protection} \cppfile{code_examples/ranges/borrowed_optin_code.h} \end{box-note} -To opt-in your ranges to work as temporaries, you need to specialize the enable\_borrowed\_range constant: +\section{Views} +One of the core problems with algorithms is that they are not easily composable. As a result, the code using algorithms is often quite verbose and requires additional copies when working with immutable data. -You might recognize the problem here. If we weren’t using range variants of the algorithms, the “bad” variant would crash at runtime. However, with ranges, this code will not compile. When a range-based algorithm is invoked with a temporary range that owns its elements, the algorithm will return a special iterator std::ranges::dangling. - -Note that the first variant with a std::string\_view will still work just fine. String view is a type of range that doesn’t own its elements, and its iterators are freestanding (they don’t depend on the instance of string\_view), so it is perfectly valid to pass such temporary into a range-based algorithm. - - - -\begin{box-note} -\begin{cppcode} -template -inline constexpr bool - std::ranges::enable_borrowed_range> = true; -\end{cppcode} -\end{box-note} - -\section{Composable views} - -One of the core problems with old STL algorithms is that they are not easily composable. As a result, the code using algorithms is often quite verbose and, when working with immutable data, requires additional copies. - -Views are trying to address this issue, making code that relies on standard algorithms less verbose and more explicit. - -\subsection{Views} - -Views are simply ranges that are cheap to copy and move (in constant time). Because of this, a view cannot own the elements it is viewing. One exception is std::views::single which owns the single element it is viewing. - -Views compose at compile time with the expectation that the compiler will inline the code. - -For example, the following code will print out the last three elements of the range. We first reverse the range, then take the first three elements, and finally reverse the range again (note that there is std::views::drop that does this directly). - -\begin{box-note} -\begin{cppcode} -namespace rv = std::ranges::views; -std::vector dt = {1, 2, 3, 4, 5, 6, 7}; -for (int v : rv::reverse(rv::take(rv::reverse(dt),3))) { - std::cout << v << ", "; -} -std::cout << "\n"; -\end{cppcode} -\end{box-note} - -\subsection{View closure objects} - -Because of the often deep nesting, the functional syntax of composing views can be cumbersome to write and read. - -Fortunately, ranges bring us another approach for compositing views. Views in the std::views namespace are actually view closure objects. These are inline constexpr constants with each std::ranges::xxx\_view mapping to an std::views::xxx object. These objects overload the operator() for functional syntax as seen above and operator| for pipe-style compositing. - -\begin{box-note} -\begin{cppcode} -namespace rv = std::ranges::views; -std::vector dt = {1, 2, 3, 4, 5, 6, 7}; -for (int v : dt | rv::reverse | rv::take(3) | rv::reverse) { - std::cout << v << ", "; -} -std::cout << "\n"; -\end{cppcode} -\end{box-note} - -Note that while views do not own their elements, they do not change the mutability of underlying data. Here, we iterate over odd elements of the array and multiply them by two. - -\begin{box-note} -\begin{cppcode} -namespace rv = std::ranges::views; -std::vector dt = {1, 2, 3, 4, 5, 6, 7}; -auto odd = [](std::integral auto v) { return v % 2 == 1; }; -for (auto& v : dt | rv::filter(odd)) { - v *= 2; -} -\end{cppcode} -\end{box-note} - -\section{FizzBuzz three ways} - -Let’s have a look at some concrete examples of Ranges. We will write three versions of FizzBuzz: - -\begin{itemize} - \item a range-ified coroutine generator - \item a generative approach using algorithms - \item a composition approach using views -\end{itemize} - -As mentioned at the beginning of the article, the current support in C++20 is a bit lacking. Therefore, I will rely on the range v3 library. - -\subsection{Coroutine generator} - -Writing a coroutine FizzBuzz generator is almost identical to the typical implementation: - -\begin{box-note} -\begin{cppcode} -ranges::experimental::generator fizzbuzz() { - for (int i = 1; ; i++) { - std::string result; - if (i % 3 == 0) result += "Fizz"; - if (i % 5 == 0) result += "Buzz"; - if (result.empty()) co_yield std::to_string(i); - else co_yield result; - } -} -\end{cppcode} -\end{box-note} - -However, if we use the generator<> from range v3 library, we can also use the invoked coroutine as a range. - -\begin{box-note} -\begin{cppcode} -for (auto s : fizzbuzz() | ranges::views::take(20)) { - std::cout << s << "\n"; -} -\end{cppcode} -\end{box-note} - -The main magic here is in the implementation of the iterator type (note this code is not from range v3 library). - -\begin{box-note} -\begin{cppcode} -// Resume coroutine to generate new value. -void operator++() { - coro_.resume(); -} -// Grab current value from coroutine. -const T& operator*() const { - return *coro_.promise().current_value; -} -// We are at the end if the coroutine is finished. -bool operator==(std::default_sentinel_t) const { - return !coro_ || coro_.done(); -} -\end{cppcode} -\end{box-note} - -The std::default\_sentinel\_t is a convenience type provided by the standard, intended to be used for distinguishing comparisons against the end(). With this, we simply need to return this iterator from the generator<> return type: - -\begin{box-note} -\begin{cppcode} -Iter begin() { - if (coro_) { - coro_.resume(); - } - return Iter{cor_}; -} -std::default_sentinel_t end() { - return {}; -} -\end{cppcode} -\end{box-note} - -\subsection{Generating using algorithms} - -We have quite a few options for the generative approach, the most obvious one being generate\_n that will allow us to generate the output directly. - -\begin{box-note} -\begin{cppcode} -ranges::generate_n( - std::ostream_iterator(std::cout, "\n"), - 20, - [i = 0]() mutable { - i++; - std::string result; - if (i % 3 == 0) result += "Fizz"; - if (i % 5 == 0) result += "Buzz"; - if (result.empty()) return std::to_string(i); - return result; -}); -\end{cppcode} -\end{box-note} - -\subsection{Compositing using views} - -Both of the previous approaches are very similar. They both implement FizzBuzz procedurally. However, we can also implement FizzBuzz in a completely different way. - -FizzBuzz includes two cycles. Fizz with a period of three and Buzz with a period of five. +Views aim to address this problem by providing cheap to copy and move facilities composed at compile-time. \begin{box-note} -\begin{cppcode} -std::array fizz{"", "", "Fizz"}; -std::array buzz{"", "", "", "", "Buzz"}; -\end{cppcode} -\end{box-note} - -First, we need to turn these cycles into infinite ranges. - -\begin{box-note} -\begin{cppcode} -const auto inf_fizz = fizz | ranges::views::cycle; -const auto inf_buzz = buzz | ranges::views::cycle; -\end{cppcode} -\end{box-note} - -Then we can combine them using zip\_with: - -\begin{box-note} -\begin{cppcode} -const auto inf_fizzbuzz = ranges::views::zip_with( - std::plus<>(), - inf_fizz, - inf_buzz); -\end{cppcode} +\footnotesize Example of composing several views. +\tcblower +\cppfile{code_examples/ranges/view_demo_code.h} \end{box-note} -Now we have an infinite range where each 3rd element is “Fizz”, each 5th element is “Buzz”, each 15th element is “FizzBuzz”, and the rest are empty strings. +In this chapter, we will go over all the views the standard library offers. -We are missing the plain numbers for the elements that are neither Fizz of Buzz. So let’s construct an infinite range of indices (starting at one): -\begin{box-note} -\begin{cppcode} -const auto indices = ranges::views::indices - | ranges::views::drop(1); -\end{cppcode} -\end{box-note} +\subsection{\texorpdfstring{\cpp{std::views::keys}, \cpp{std::views::values}}{\texttt{std::views::keys}, \texttt{std::views::values}}} +\index{\cpp{std::views::keys}} +\index{\cpp{std::views::values}} -And finally, we need to put these two ranges together and output the final result. - -\begin{box-note} -\begin{cppcode} -const auto final_range = ranges::views::zip_with( - [](auto i, auto s) { - if (s.empty()) return std::to_string(i); - return s; - }, - indices, - inf_fizzbuzz -); -ranges::copy_n(ranges::begin(final_range), 20, - std::ostream_iterator(std::cout, "\n")); -\end{cppcode} -\end{box-note} \ No newline at end of file +\subsection{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elements}}} +\index{\cpp{std::views::elements}} \ No newline at end of file diff --git a/chapters/Y7_cpp20_views.tex b/chapters/Y7_cpp20_views.tex deleted file mode 100644 index e69de29..0000000 From 1ff93c67545719ac74cd1d99b597a5294bbb01c1 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 10:11:27 +0200 Subject: [PATCH 21/96] Example for views::keys and views::values. --- code_examples/CMakeLists.txt | 1 + code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/keys_values.cpp | 17 +++++++++++++++++ code_examples/views/keys_values_code.h | 11 +++++++++++ 4 files changed, 33 insertions(+) create mode 100644 code_examples/views/CMakeLists.txt create mode 100644 code_examples/views/keys_values.cpp create mode 100644 code_examples/views/keys_values_code.h diff --git a/code_examples/CMakeLists.txt b/code_examples/CMakeLists.txt index 36f5e92..2464471 100644 --- a/code_examples/CMakeLists.txt +++ b/code_examples/CMakeLists.txt @@ -14,3 +14,4 @@ add_subdirectory(algorithms) add_subdirectory(theory) add_subdirectory(extras) add_subdirectory(ranges) +add_subdirectory(views) diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt new file mode 100644 index 0000000..e8b68c0 --- /dev/null +++ b/code_examples/views/CMakeLists.txt @@ -0,0 +1,4 @@ +# Doesn't compile with GCC 11 +#add_executable(keys_values keys_values.cpp) +#install(TARGETS keys_values DESTINATION bin) + diff --git a/code_examples/views/keys_values.cpp b/code_examples/views/keys_values.cpp new file mode 100644 index 0000000..3d0601b --- /dev/null +++ b/code_examples/views/keys_values.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include +#include + +int main() { +#include "keys_values_code.h" + + auto cmp1 = {0, 1, 2, 3}; + auto cmp2 = {1.0, 1.5, 2.0, 2.5}; + assert(std::ranges::is_permutation(keys, cmp1)); + assert(std::ranges::is_permutation(values, cmp2)); + + std::cerr << "."; +} diff --git a/code_examples/views/keys_values_code.h b/code_examples/views/keys_values_code.h new file mode 100644 index 0000000..2a63d88 --- /dev/null +++ b/code_examples/views/keys_values_code.h @@ -0,0 +1,11 @@ +std::unordered_map map{ + {0, 1.0}, {1, 1.5}, {2, 2.0}, {3, 2.5} +}; + +std::vector keys; +std::ranges::copy(std::views::keys(map), std::back_inserter(keys)); +// keys == {0, 1, 2, 3} in unspecified order (std::unordered_map) + +std::vector values; +std::ranges::copy(std::views::values(map), std::back_inserter(values)); +// values == {1.0, 1.5, 2.0, 2.5} in unspecified order matching order of keys From 43f6e61aca1ec5830d770c7d2d6431bb5f4be129 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 10:11:48 +0200 Subject: [PATCH 22/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 98ff7ea..82deb0e 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -113,5 +113,15 @@ \subsection{\texorpdfstring{\cpp{std::views::keys}, \cpp{std::views::values}}{\t \index{\cpp{std::views::keys}} \index{\cpp{std::views::values}} +The \cpp{std::views::keys} and \cpp{std::views::values} operate on ranges of pair-like elements. +The \cpp{std::views::keys} will produce a range of the first elements from each pair. +The \cpp{std::views::values} will produce a range of the second elements from each pair. + +\begin{box-note} +\footnotesize Example of decomposing a \cpp{std::unordered_map} into a view over the keys and a view over the values. +\tcblower +\cppfile{code_examples/ranges/key_value_code.h} +\end{box-note} + \subsection{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elements}}} \index{\cpp{std::views::elements}} \ No newline at end of file From ccb4b9a6bc72e1e82cc5bd659d99fd180a0f69a2 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 14:33:00 +0200 Subject: [PATCH 23/96] Example for views::elements. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/elements.cpp | 15 +++++++++++++++ code_examples/views/elements_code.h | 11 +++++++++++ code_examples/views/keys_values_code.h | 6 ++++-- 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 code_examples/views/elements.cpp create mode 100644 code_examples/views/elements_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index e8b68c0..d7f634b 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -2,3 +2,7 @@ #add_executable(keys_values keys_values.cpp) #install(TARGETS keys_values DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(elements elements.cpp) +#install(TARGETS elements DESTINATION bin) diff --git a/code_examples/views/elements.cpp b/code_examples/views/elements.cpp new file mode 100644 index 0000000..736b07c --- /dev/null +++ b/code_examples/views/elements.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include +#include + +int main() { +#include "elements_code.h" + std::vector cmp1 = {"Cat", "Dog", "Car"}; + assert(std::ranges::equal(third, cmp1)); + auto cmp2 = {100, 99, 17}; + assert(std::ranges::equal(second, cmp2)); + std::cerr << "."; +} diff --git a/code_examples/views/elements_code.h b/code_examples/views/elements_code.h new file mode 100644 index 0000000..4c2e0f3 --- /dev/null +++ b/code_examples/views/elements_code.h @@ -0,0 +1,11 @@ +std::vector> data{ + {1, 100, "Cat"}, {2, 99, "Dog"}, {3, 17, "Car"}, +}; + +std::vector second; +std::ranges::copy(data | std::views::elements<1>, std::back_inserter(second)); +// second == {100, 99, 17} + +std::vector third; +std::ranges::copy(data | std::views::elements<2>, std::back_inserter(third)); +// third == {"Cat", "Dog", "Car"} diff --git a/code_examples/views/keys_values_code.h b/code_examples/views/keys_values_code.h index 2a63d88..1544036 100644 --- a/code_examples/views/keys_values_code.h +++ b/code_examples/views/keys_values_code.h @@ -7,5 +7,7 @@ std::ranges::copy(std::views::keys(map), std::back_inserter(keys)); // keys == {0, 1, 2, 3} in unspecified order (std::unordered_map) std::vector values; -std::ranges::copy(std::views::values(map), std::back_inserter(values)); -// values == {1.0, 1.5, 2.0, 2.5} in unspecified order matching order of keys +std::ranges::copy(std::views::values(map), + std::back_inserter(values)); +// values == {1.0, 1.5, 2.0, 2.5} +// in unspecified order matching order of keys From 858e70f7ddeced92b89ff53858534a56aeb062e4 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 14:33:27 +0200 Subject: [PATCH 24/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 82deb0e..7b37fff 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -120,8 +120,16 @@ \subsection{\texorpdfstring{\cpp{std::views::keys}, \cpp{std::views::values}}{\t \begin{box-note} \footnotesize Example of decomposing a \cpp{std::unordered_map} into a view over the keys and a view over the values. \tcblower -\cppfile{code_examples/ranges/key_value_code.h} +\cppfile{code_examples/views/keys_values_code.h} \end{box-note} \subsection{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elements}}} -\index{\cpp{std::views::elements}} \ No newline at end of file +\index{\cpp{std::views::elements}} + +The \cpp{std::views::elements} will produce a range of the Nth elements from a range of tuple-like elements. + +\begin{box-note} +\footnotesize Example of creating element views for each tuple's second and third elements in the source range. +\tcblower +\cppfile{code_examples/views/elements_code.h} +\end{box-note} \ No newline at end of file From 0114da49242c63ea02b454eb9ca5e43e14fb2fec Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 15:01:22 +0200 Subject: [PATCH 25/96] Examples for views::take and views::take_while. --- code_examples/views/CMakeLists.txt | 5 ++++- code_examples/views/take.cpp | 14 ++++++++++++++ code_examples/views/take_code.h | 12 ++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 code_examples/views/take.cpp create mode 100644 code_examples/views/take_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index d7f634b..1a58ef6 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -2,7 +2,10 @@ #add_executable(keys_values keys_values.cpp) #install(TARGETS keys_values DESTINATION bin) - # Doesn't compile with GCC 11 #add_executable(elements elements.cpp) #install(TARGETS elements DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(take take.cpp) +#install(TARGETS take DESTINATION bin) diff --git a/code_examples/views/take.cpp b/code_examples/views/take.cpp new file mode 100644 index 0000000..0f83c46 --- /dev/null +++ b/code_examples/views/take.cpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include +#include +#include + +int main() { +#include "take_code.h" + auto cmp1 = {1, 3, 5}; + assert(std::ranges::equal(out1, cmp1)); + auto cmp2 = {1, 3, 5, 7}; + assert(std::ranges::equal(out2, cmp2)); +} diff --git a/code_examples/views/take_code.h b/code_examples/views/take_code.h new file mode 100644 index 0000000..36c03c7 --- /dev/null +++ b/code_examples/views/take_code.h @@ -0,0 +1,12 @@ +std::vector data{1, 3, 5, 7, 2, 4, 6, 8}; + +std::vector out1; +std::ranges::copy(data | std::views::take(3), + std::back_inserter(out1)); +// out1 == {1, 3, 5} + +std::vector out2; +std::ranges::copy(data | + std::views::take_while([](int v) { return v % 2 != 0; }), + std::back_inserter(out2)); +// out2 == {1, 3, 5, 7} From 2a91acee80fc7a642a56e83bb82171416b5baf75 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 15:02:47 +0200 Subject: [PATCH 26/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 7b37fff..4a1fd63 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -132,4 +132,14 @@ \subsection{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elem \footnotesize Example of creating element views for each tuple's second and third elements in the source range. \tcblower \cppfile{code_examples/views/elements_code.h} +\end{box-note} + +\subsection{\texorpdfstring{\cpp{std::views::take}, \cpp{std::views::take_while}}{\texttt{std::views::take}, \texttt{std::views::take\_while}}} + +Both views are formed from the leading elements of the source range. In the case of \cpp{std::views::take}, the view consists of the first N elements. In the case of \cpp{std::views::take_while}, the view consists of the sequence of elements for which the predicate evaluates true. + +\begin{box-note} +\footnotesize Example of a view of the first three elements and a view of the leading sequence of odd elements. +\tcblower +\cppfile{code_examples/views/take_code.h} \end{box-note} \ No newline at end of file From 207eacc7c85a29249346a991f6d12d34b6c3c6b1 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 15:19:43 +0200 Subject: [PATCH 27/96] Example for views::drop nad views::drop_while. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/drop.cpp | 14 ++++++++++++++ code_examples/views/drop_code.h | 12 ++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 code_examples/views/drop.cpp create mode 100644 code_examples/views/drop_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index 1a58ef6..0c00ffd 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -9,3 +9,7 @@ # Doesn't compile with GCC 11 #add_executable(take take.cpp) #install(TARGETS take DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(drop drop.cpp) +#install(TARGETS drop DESTINATION bin) diff --git a/code_examples/views/drop.cpp b/code_examples/views/drop.cpp new file mode 100644 index 0000000..70c513d --- /dev/null +++ b/code_examples/views/drop.cpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include +#include +#include + +int main() { +#include "drop_code.h" + auto cmp1 = {7, 2, 4, 6, 8}; + assert(std::ranges::equal(out1, cmp1)); + auto cmp2 = {2, 4, 6, 8}; + assert(std::ranges::equal(out2, cmp2)); +} diff --git a/code_examples/views/drop_code.h b/code_examples/views/drop_code.h new file mode 100644 index 0000000..09425eb --- /dev/null +++ b/code_examples/views/drop_code.h @@ -0,0 +1,12 @@ +std::vector data{1, 3, 5, 7, 2, 4, 6, 8}; + +std::vector out1; +std::ranges::copy(data | std::views::drop(3), + std::back_inserter(out1)); +// out1 == {7, 2, 4, 6, 8} + +std::vector out2; +std::ranges::copy(data | + std::views::drop_while([](int v) { return v % 2 != 0; }), + std::back_inserter(out2)); +// out2 == {2, 4, 6, 8} From c7da7af28634a7606b38b682fe349cec1a3a86f7 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 27 Jul 2022 15:21:00 +0200 Subject: [PATCH 28/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 4a1fd63..b2fc2d9 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -142,4 +142,16 @@ \subsection{\texorpdfstring{\cpp{std::views::take}, \cpp{std::views::take_while} \footnotesize Example of a view of the first three elements and a view of the leading sequence of odd elements. \tcblower \cppfile{code_examples/views/take_code.h} +\end{box-note} + +\subsection{\texorpdfstring{\cpp{std::views::drop}, \cpp{std::views::drop_while}}{\texttt{std::views::drop}, \texttt{std::views::drop\_while}}} + +The drop views are the inverse of take views. +The \cpp{std::views::drop} consists of all but the first N elements. +The \cpp{std::views::drop_while} consists of all but the leading sequence of elements for which the predicate evaluates true. + +\begin{box-note} +\footnotesize Example of a view of all but the first three elements and a view skipping over the leading sequence of odd elements. +\tcblower +\cppfile{code_examples/views/drop_code.h} \end{box-note} \ No newline at end of file From e4918645d4e1b711c13d0ead9deb0a63efe0f868 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 10:47:36 +0200 Subject: [PATCH 29/96] Example for views::filter. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/filter.cpp | 13 +++++++++++++ code_examples/views/filter_code.h | 7 +++++++ 3 files changed, 24 insertions(+) create mode 100644 code_examples/views/filter.cpp create mode 100644 code_examples/views/filter_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index 0c00ffd..3aa5fd1 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -13,3 +13,7 @@ # Doesn't compile with GCC 11 #add_executable(drop drop.cpp) #install(TARGETS drop DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(filter filter.cpp) +#install(TARGETS filter DESTINATION bin) diff --git a/code_examples/views/filter.cpp b/code_examples/views/filter.cpp new file mode 100644 index 0000000..3b09816 --- /dev/null +++ b/code_examples/views/filter.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include +#include + +int main() { +#include "filter_code.h" + auto cmp1 = {2, 4, 6, 8}; + assert(std::ranges::equal(even, cmp1)); + std::cerr << "."; +} diff --git a/code_examples/views/filter_code.h b/code_examples/views/filter_code.h new file mode 100644 index 0000000..8231228 --- /dev/null +++ b/code_examples/views/filter_code.h @@ -0,0 +1,7 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7, 8}; + +std::vector even; +std::ranges::copy(data | + std::views::filter([](int v) { return v % 2 == 0; }), + std::back_inserter(even)); +// even == {2, 4, 6, 8} From f9b5a26a0726b64c6f7023ecaf184a559dddffc5 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 10:48:28 +0200 Subject: [PATCH 30/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index b2fc2d9..40f17f1 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -135,6 +135,8 @@ \subsection{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elem \end{box-note} \subsection{\texorpdfstring{\cpp{std::views::take}, \cpp{std::views::take_while}}{\texttt{std::views::take}, \texttt{std::views::take\_while}}} +\index{\cpp{std::views::take}} +\index{\cpp{std::views::take_while}} Both views are formed from the leading elements of the source range. In the case of \cpp{std::views::take}, the view consists of the first N elements. In the case of \cpp{std::views::take_while}, the view consists of the sequence of elements for which the predicate evaluates true. @@ -145,6 +147,8 @@ \subsection{\texorpdfstring{\cpp{std::views::take}, \cpp{std::views::take_while} \end{box-note} \subsection{\texorpdfstring{\cpp{std::views::drop}, \cpp{std::views::drop_while}}{\texttt{std::views::drop}, \texttt{std::views::drop\_while}}} +\index{\cpp{std::views::drop}} +\index{\cpp{std::views::drop_while}} The drop views are the inverse of take views. The \cpp{std::views::drop} consists of all but the first N elements. @@ -154,4 +158,18 @@ \subsection{\texorpdfstring{\cpp{std::views::drop}, \cpp{std::views::drop_while} \footnotesize Example of a view of all but the first three elements and a view skipping over the leading sequence of odd elements. \tcblower \cppfile{code_examples/views/drop_code.h} -\end{box-note} \ No newline at end of file +\end{box-note} + +\subsection{\texorpdfstring{\cpp{std::views::filter}}{\texttt{std::views::filter}}} + +The filter view consists of all elements that satisfy the provided predicate. + +\begin{box-note} +\footnotesize Example of a view of even elements. +\tcblower +\cppfile{code_examples/views/filter_code.h} +\end{box-note} + + +\subsection{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::counted}}} +\subsection{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::reverse}}} From 8f6ab5889a1d4277f0b047ac5b77d717e621feb9 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 10:54:48 +0200 Subject: [PATCH 31/96] Example for views::reverse --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/reverse.cpp | 12 ++++++++++++ code_examples/views/reverse_code.h | 6 ++++++ 3 files changed, 22 insertions(+) create mode 100644 code_examples/views/reverse.cpp create mode 100644 code_examples/views/reverse_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index 3aa5fd1..c0baa64 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -17,3 +17,7 @@ # Doesn't compile with GCC 11 #add_executable(filter filter.cpp) #install(TARGETS filter DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(reverse_view reverse.cpp) +#install(TARGETS reverse_view DESTINATION bin) diff --git a/code_examples/views/reverse.cpp b/code_examples/views/reverse.cpp new file mode 100644 index 0000000..6dd3adc --- /dev/null +++ b/code_examples/views/reverse.cpp @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include +#include + +int main() { +#include "reverse_code.h" + auto cmp1 = {4, 3, 2, 1}; + assert(std::ranges::equal(out, cmp1)); +} diff --git a/code_examples/views/reverse_code.h b/code_examples/views/reverse_code.h new file mode 100644 index 0000000..501a35f --- /dev/null +++ b/code_examples/views/reverse_code.h @@ -0,0 +1,6 @@ +std::vector data{1, 2, 3, 4}; + +std::vector out; +std::ranges::copy(data | std::views::reverse, + std::back_inserter(out)); +// even == {4, 3, 2, 1} From b5b7c91c5cedd88bca81fa13f13bb74d5aad0670 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 10:56:05 +0200 Subject: [PATCH 32/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 40f17f1..42dc23b 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -170,6 +170,18 @@ \subsection{\texorpdfstring{\cpp{std::views::filter}}{\texttt{std::views::filter \cppfile{code_examples/views/filter_code.h} \end{box-note} +\subsection{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::reverse}}} + +The reverse view is the reverse iteration view for bidirectional ranges. + +\begin{box-note} +\footnotesize Example of a reverse view. +\tcblower +\cppfile{code_examples/views/reverse_code.h} +\end{box-note} + \subsection{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::counted}}} -\subsection{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::reverse}}} + + + From a77f4cf45fbac2015ab5db8da6bde0e98c34fcad Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 11:14:58 +0200 Subject: [PATCH 33/96] Example for counted view. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/counted.cpp | 12 ++++++++++++ code_examples/views/counted_code.h | 6 ++++++ code_examples/views/reverse_code.h | 2 +- 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 code_examples/views/counted.cpp create mode 100644 code_examples/views/counted_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index c0baa64..026d805 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -21,3 +21,7 @@ # Doesn't compile with GCC 11 #add_executable(reverse_view reverse.cpp) #install(TARGETS reverse_view DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(counted_view counted.cpp) +#install(TARGETS counted_view DESTINATION bin) diff --git a/code_examples/views/counted.cpp b/code_examples/views/counted.cpp new file mode 100644 index 0000000..eee80a3 --- /dev/null +++ b/code_examples/views/counted.cpp @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include +#include + +int main() { +#include "counted_code.h" + auto cmp1 = {2, 3, 4}; + assert(std::ranges::equal(out, cmp1)); +} diff --git a/code_examples/views/counted_code.h b/code_examples/views/counted_code.h new file mode 100644 index 0000000..49f2233 --- /dev/null +++ b/code_examples/views/counted_code.h @@ -0,0 +1,6 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7, 8}; + +std::vector out; +std::ranges::copy(std::views::counted(std::next(data.begin()), 3), + std::back_inserter(out)); +// out == {2, 3, 4} diff --git a/code_examples/views/reverse_code.h b/code_examples/views/reverse_code.h index 501a35f..c7e0e0d 100644 --- a/code_examples/views/reverse_code.h +++ b/code_examples/views/reverse_code.h @@ -3,4 +3,4 @@ std::vector data{1, 2, 3, 4}; std::vector out; std::ranges::copy(data | std::views::reverse, std::back_inserter(out)); -// even == {4, 3, 2, 1} +// out == {4, 3, 2, 1} From 9f8945290d6f8e10772b87b13266081fdb850c16 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 11:55:15 +0200 Subject: [PATCH 34/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 42dc23b..f2f2224 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -180,8 +180,14 @@ \subsection{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::rever \cppfile{code_examples/views/reverse_code.h} \end{box-note} - \subsection{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::counted}}} +The counted view adapts an iterator and number of elements into a view. + +\subsection{\texorpdfstring{\cpp{std::views::common}}{\texttt{std::views::common}}} + +The common view adapts a view into a common range, a range with a begin and end iterator of matching types. Non-range versions of algorithms require a common range. +\subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} +The all view is a view of all the elements from a range. \ No newline at end of file From b5dfe1bd200b992eb0b5f1322379db3bd42582bb Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 12:27:26 +0200 Subject: [PATCH 35/96] Example for common view. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/common.cpp | 12 ++++++++++++ code_examples/views/common_code.h | 9 +++++++++ 3 files changed, 25 insertions(+) create mode 100644 code_examples/views/common.cpp create mode 100644 code_examples/views/common_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index 026d805..e09b2ed 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -25,3 +25,7 @@ # Doesn't compile with GCC 11 #add_executable(counted_view counted.cpp) #install(TARGETS counted_view DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(common_view common.cpp) +#install(TARGETS common_view DESTINATION bin) diff --git a/code_examples/views/common.cpp b/code_examples/views/common.cpp new file mode 100644 index 0000000..2f802cb --- /dev/null +++ b/code_examples/views/common.cpp @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include +#include + +int main() { +#include "common_code.h" + auto cmp1 = {2, 4, 6, 8}; + assert(std::ranges::equal(out, cmp1)); +} diff --git a/code_examples/views/common_code.h b/code_examples/views/common_code.h new file mode 100644 index 0000000..4562f8d --- /dev/null +++ b/code_examples/views/common_code.h @@ -0,0 +1,9 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7, 8}; + +std::vector out; +auto view = data | + std::views::filter([](int v) { return v % 2 == 0; }) | + std::views::common; + +std::copy(view.begin(), view.end(), std::back_inserter(out)); +// out == {2, 4, 6, 8} From cd1aee8d029ef174eff8e0ec302f082ea5c2fcfe Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 12:28:48 +0200 Subject: [PATCH 36/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index f2f2224..a2c32ca 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -184,10 +184,22 @@ \subsection{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::count The counted view adapts an iterator and number of elements into a view. +\begin{box-note} +\footnotesize Example of using a counted view to iterate over a subrange. +\tcblower +\cppfile{code_examples/views/counted_code.h} +\end{box-note} + \subsection{\texorpdfstring{\cpp{std::views::common}}{\texttt{std::views::common}}} The common view adapts a view into a common range, a range with a begin and end iterator of matching types. Non-range versions of algorithms require a common range. +\begin{box-note} +\footnotesize Example of using adapting a view for a non-range algorithm. +\tcblower +\cppfile{code_examples/views/common_code.h} +\end{box-note} + \subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} The all view is a view of all the elements from a range. \ No newline at end of file From 3a7fd3aac1077ef3fac3ad28c41298e0c3be79ef Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 12:59:07 +0200 Subject: [PATCH 37/96] Example for all view. --- code_examples/views/all.cpp | 12 ++++++++++++ code_examples/views/all_code.h | 5 +++++ 2 files changed, 17 insertions(+) create mode 100644 code_examples/views/all.cpp create mode 100644 code_examples/views/all_code.h diff --git a/code_examples/views/all.cpp b/code_examples/views/all.cpp new file mode 100644 index 0000000..01bae3b --- /dev/null +++ b/code_examples/views/all.cpp @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include +#include + +int main() { +#include "all_code.h" + auto cmp1 = {1, 2, 3, 4}; + assert(std::ranges::equal(out, cmp1)); +} diff --git a/code_examples/views/all_code.h b/code_examples/views/all_code.h new file mode 100644 index 0000000..000994b --- /dev/null +++ b/code_examples/views/all_code.h @@ -0,0 +1,5 @@ +std::vector data{1, 2, 3, 4}; + +std::vector out; +std::ranges::copy(std::views::all(data), std::back_inserter(out)); +// out == {1, 2, 3, 4} From 9c03b3f952adc34b16124a8677f1b6cfc034c76a Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 13:10:01 +0200 Subject: [PATCH 38/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index a2c32ca..687ae62 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -202,4 +202,10 @@ \subsection{\texorpdfstring{\cpp{std::views::common}}{\texttt{std::views::common \subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} -The all view is a view of all the elements from a range. \ No newline at end of file +The all view is a view of all the elements from a range. + +\begin{box-note} +\footnotesize Example of creating a view over all elements. Note that view over all elements is the default. +\tcblower +\cppfile{code_examples/views/all_code.h} +\end{box-note} From 2a1d402d7dd00678be232dc66d210809b7f18491 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 16:23:58 +0200 Subject: [PATCH 39/96] Example for split view. --- code_examples/views/CMakeLists.txt | 8 ++++++++ code_examples/views/split.cpp | 13 +++++++++++++ code_examples/views/split_code.h | 13 +++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 code_examples/views/split.cpp create mode 100644 code_examples/views/split_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index e09b2ed..e1ebed2 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -29,3 +29,11 @@ # Doesn't compile with GCC 11 #add_executable(common_view common.cpp) #install(TARGETS common_view DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(all_view all.cpp) +#install(TARGETS all_view DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(split_view split.cpp) +#install(TARGETS split_view DESTINATION bin) diff --git a/code_examples/views/split.cpp b/code_examples/views/split.cpp new file mode 100644 index 0000000..03651e2 --- /dev/null +++ b/code_examples/views/split.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include +#include +#include + +int main() { +#include "split_code.h" + auto cmp1 = {1, 23, 13, 42}; + assert(std::ranges::equal(parsed, cmp1)); +} diff --git a/code_examples/views/split_code.h b/code_examples/views/split_code.h new file mode 100644 index 0000000..a71b5c8 --- /dev/null +++ b/code_examples/views/split_code.h @@ -0,0 +1,13 @@ +std::string version = "1.23.13.42"; +std::vector parsed; + +std::ranges::copy(version | + std::views::split('.') | + std::views::transform([](auto v) { + int result = 0; + // from_chars requires contiguous range + std::from_chars(v.data(), v.data()+v.size(), result); + return result; + }), + std::back_inserter(parsed)); +// parsed == {1, 23, 13, 42} From fa2dd850746a83af7fa20e83f7513054ec9a6edb Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 16:25:26 +0200 Subject: [PATCH 40/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 687ae62..b7b0ca4 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -209,3 +209,14 @@ \subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} \tcblower \cppfile{code_examples/views/all_code.h} \end{box-note} + +\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}}} + +The two split views split a single range into a range of sub-ranges. However, they differ in their implementation. +The \cpp{std::view::split} maintains the bidirectional, random access or continuous properties of the underlying range, and the \cpp{std::view::lazy_split} does not, but it does support input ranges. + +\begin{box-note} +\footnotesize Example of using split view to parse a version number. +\tcblower +\cppfile{code_examples/views/split_code.h} +\end{box-note} \ No newline at end of file From 72faa6cef5f89ff256e064ad752dbb66acb53aba Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 19:36:15 +0200 Subject: [PATCH 41/96] Example for views::transform. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/transform.cpp | 13 +++++++++++++ code_examples/views/transform_code.h | 8 ++++++++ 3 files changed, 25 insertions(+) create mode 100644 code_examples/views/transform.cpp create mode 100644 code_examples/views/transform_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index e1ebed2..22fc410 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -37,3 +37,7 @@ # Doesn't compile with GCC 11 #add_executable(split_view split.cpp) #install(TARGETS split_view DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(transform_view transform.cpp) +#install(TARGETS transform_view DESTINATION bin) diff --git a/code_examples/views/transform.cpp b/code_examples/views/transform.cpp new file mode 100644 index 0000000..84e112c --- /dev/null +++ b/code_examples/views/transform.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include +#include +#include + +int main() { +#include "transform_code.h" + auto cmp1 = {1, 5, 9, 20, 50, 67}; + assert(std::ranges::equal(out, cmp1)); +} diff --git a/code_examples/views/transform_code.h b/code_examples/views/transform_code.h new file mode 100644 index 0000000..304cc0f --- /dev/null +++ b/code_examples/views/transform_code.h @@ -0,0 +1,8 @@ +std::vector data{1.2, 2.3, 3.1, 4.5, 7.1, 8.2}; + +std::vector out; +std::ranges::copy(data | + std::views::transform([](auto v) -> int { + return v*v; + }), std::back_inserter(out)); +// out == {1, 5, 9, 20, 50, 67} From 7631ff92e2d5423c47b03870660540d02c947b7b Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 29 Jul 2022 19:37:17 +0200 Subject: [PATCH 42/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index b7b0ca4..c3a1e86 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -108,7 +108,6 @@ \section{Views} In this chapter, we will go over all the views the standard library offers. - \subsection{\texorpdfstring{\cpp{std::views::keys}, \cpp{std::views::values}}{\texttt{std::views::keys}, \texttt{std::views::values}}} \index{\cpp{std::views::keys}} \index{\cpp{std::views::values}} @@ -134,6 +133,17 @@ \subsection{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elem \cppfile{code_examples/views/elements_code.h} \end{box-note} +\subsection{\texorpdfstring{\cpp{std::views::transform}}{\texttt{std::views::transform}}} +\index{\cpp{std::views::transform}} + +The transform view applies a transformation functor to every element of the range. + +\begin{box-note} +\footnotesize Example of using \cpp{std::views::transform} that also changes the base type of the range. +\tcblower +\cppfile{code_examples/views/transform_code.h} +\end{box-note} + \subsection{\texorpdfstring{\cpp{std::views::take}, \cpp{std::views::take_while}}{\texttt{std::views::take}, \texttt{std::views::take\_while}}} \index{\cpp{std::views::take}} \index{\cpp{std::views::take_while}} @@ -161,6 +171,7 @@ \subsection{\texorpdfstring{\cpp{std::views::drop}, \cpp{std::views::drop_while} \end{box-note} \subsection{\texorpdfstring{\cpp{std::views::filter}}{\texttt{std::views::filter}}} +\index{\cpp{std::views::filter}} The filter view consists of all elements that satisfy the provided predicate. @@ -171,6 +182,7 @@ \subsection{\texorpdfstring{\cpp{std::views::filter}}{\texttt{std::views::filter \end{box-note} \subsection{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::reverse}}} +\index{\cpp{std::views::reverse}} The reverse view is the reverse iteration view for bidirectional ranges. @@ -181,6 +193,7 @@ \subsection{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::rever \end{box-note} \subsection{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::counted}}} +\index{\cpp{std::views::counted}} The counted view adapts an iterator and number of elements into a view. @@ -191,6 +204,7 @@ \subsection{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::count \end{box-note} \subsection{\texorpdfstring{\cpp{std::views::common}}{\texttt{std::views::common}}} +\index{\cpp{std::views::common}} The common view adapts a view into a common range, a range with a begin and end iterator of matching types. Non-range versions of algorithms require a common range. @@ -201,6 +215,7 @@ \subsection{\texorpdfstring{\cpp{std::views::common}}{\texttt{std::views::common \end{box-note} \subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} +\index{\cpp{std::views::all}} The all view is a view of all the elements from a range. @@ -211,9 +226,11 @@ \subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} \end{box-note} \subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}}} +\index{\cpp{std::views::split}} +\index{\cpp{std::views::lazy_split}} The two split views split a single range into a range of sub-ranges. However, they differ in their implementation. -The \cpp{std::view::split} maintains the bidirectional, random access or continuous properties of the underlying range, and the \cpp{std::view::lazy_split} does not, but it does support input ranges. +The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \cpp{std::view::lazy_split} does not, but it does support input ranges. \begin{box-note} \footnotesize Example of using split view to parse a version number. From da32c8fe1279739e46c2ab057cf90729ad6db6a0 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 10:36:33 +0200 Subject: [PATCH 43/96] Example for lazy_split and join views. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/join.cpp | 16 ++++++++++++++++ code_examples/views/join_code.h | 13 +++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 code_examples/views/join.cpp create mode 100644 code_examples/views/join_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index 22fc410..495205f 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -41,3 +41,7 @@ # Doesn't compile with GCC 11 #add_executable(transform_view transform.cpp) #install(TARGETS transform_view DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(join_view join.cpp) +#install(TARGETS join_view DESTINATION bin) diff --git a/code_examples/views/join.cpp b/code_examples/views/join.cpp new file mode 100644 index 0000000..57a13f1 --- /dev/null +++ b/code_examples/views/join.cpp @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main() { +#include "join_code.h" + assert(out == "CarDogWindow"); + std::vector cmp = {"Car", "Dog", "Window"}; + assert(std::ranges::equal(cmp, words)); +} diff --git a/code_examples/views/join_code.h b/code_examples/views/join_code.h new file mode 100644 index 0000000..803dd0e --- /dev/null +++ b/code_examples/views/join_code.h @@ -0,0 +1,13 @@ +std::string_view data = "Car Dog Window"; +std::vector words; +std::ranges::for_each(data | std::views::lazy_split(' '), + [&words](auto const& view) { + // string constructor needs common range. + auto common = view | std::views::common; + words.emplace_back(common.begin(), common.end()); + }); +// words == {"Car", "Dog", "Window"} + +auto joined = data | std::views::lazy_split(' ') | std::views::join | std::views::common; +std::string out(joined.begin(), joined.end()); +// out == "CarDogWindow" From 2e8523c255cf9ffdcd60f17f24fdd08dab99e99e Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 10:38:07 +0200 Subject: [PATCH 44/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index c3a1e86..831ee92 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -225,15 +225,28 @@ \subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} \cppfile{code_examples/views/all_code.h} \end{box-note} -\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}}} +\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \texorpdfstring{\cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}}, \texttt{std::views::join\_view}}} \index{\cpp{std::views::split}} \index{\cpp{std::views::lazy_split}} +\index{\cpp{std::views::join_view}} -The two split views split a single range into a range of sub-ranges. However, they differ in their implementation. +The two split views split a single range into a view over sub-ranges. However, they differ in their implementation. The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \cpp{std::view::lazy_split} does not, but it does support input ranges. \begin{box-note} \footnotesize Example of using split view to parse a version number. \tcblower \cppfile{code_examples/views/split_code.h} -\end{box-note} \ No newline at end of file +\end{box-note} + +The join view flattens a view of ranges. + +\begin{box-note} +\footnotesize Example of using lazy_split view to split a string into tokens and then join them using the join view. +\tcblower +\cppfile{code_examples/views/join_code.h} +\end{box-note} + + + + From 69928dccd0cdf125b5f7353e1c0ce192dfdf499e Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 12:00:54 +0200 Subject: [PATCH 45/96] Example for empty and single view. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/single.cpp | 11 +++++++++++ code_examples/views/single_code.h | 6 ++++++ 3 files changed, 21 insertions(+) create mode 100644 code_examples/views/single.cpp create mode 100644 code_examples/views/single_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index 495205f..c8eb7c0 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -45,3 +45,7 @@ # Doesn't compile with GCC 11 #add_executable(join_view join.cpp) #install(TARGETS join_view DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(single_view single.cpp) +#install(TARGETS single_view DESTINATION bin) diff --git a/code_examples/views/single.cpp b/code_examples/views/single.cpp new file mode 100644 index 0000000..b751776 --- /dev/null +++ b/code_examples/views/single.cpp @@ -0,0 +1,11 @@ +#include +#include +#include +#include +#include + +int main() { +#include "single_code.h" + assert(out.size() == 1); + assert(out[0] == 42); +} diff --git a/code_examples/views/single_code.h b/code_examples/views/single_code.h new file mode 100644 index 0000000..ba4adb5 --- /dev/null +++ b/code_examples/views/single_code.h @@ -0,0 +1,6 @@ +std::vector out; +std::ranges::copy(std::views::empty, std::back_inserter(out)); +// out == {} + +std::ranges::copy(std::views::single(42), std::back_inserter(out)); +// out == {42} From c6b049ba8188df4450dd0138d9cedc354b31cb39 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 12:02:32 +0200 Subject: [PATCH 46/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 831ee92..060f9fc 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -225,7 +225,7 @@ \subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} \cppfile{code_examples/views/all_code.h} \end{box-note} -\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \texorpdfstring{\cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}}, \texttt{std::views::join\_view}}} +\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}, \texttt{std::views::join\_view}}} \index{\cpp{std::views::split}} \index{\cpp{std::views::lazy_split}} \index{\cpp{std::views::join_view}} @@ -242,11 +242,31 @@ \subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split The join view flattens a view of ranges. \begin{box-note} -\footnotesize Example of using lazy_split view to split a string into tokens and then join them using the join view. +\footnotesize Example of using \cpp{std::views::lazy_split} to split a string into tokens and then join them using the \cpp{std::views::join}. \tcblower \cppfile{code_examples/views/join_code.h} \end{box-note} +\subsection{\texorpdfstring{\cpp{std::views::empty}, \cpp{std::views::single}}{\texttt{std::views::empty}, \texttt{std::views::single}}} +\index{\cpp{std::views::empty}} +\index{\cpp{std::views::single}} +The empty view is an empty view (containing no elements), and the single view is a view that contains a single element. +Note that the view owns the single element, which will be copied/moved alongside the view. +\begin{box-note} +\footnotesize Example of using \cpp{std::views::empty} and \cpp{std::views::single}. +\tcblower +\cppfile{code_examples/views/single_code.h} +\end{box-note} + +\subsection{\texorpdfstring{\cpp{std::views::iota}}{\texttt{std::views::iota}}} +\index{\cpp{std::views::iota}} + +The iota view represents a generated sequence formed by repeatedly incrementing an initial value. + +\subsection{\texorpdfstring{\cpp{std::views::istream}}{\texttt{std::views::istream}}} +\index{\cpp{std::views::istream}} +The istream view provides similar functionality to istream iterator, except in the form of a view. +It represents a view obtained by successively applying the istream input operator. \ No newline at end of file From fb365640aad8bce0152d7aa3dde3ef427834f216 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 12:50:24 +0200 Subject: [PATCH 47/96] Example for iota view. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/iota.cpp | 13 +++++++++++++ code_examples/views/iota_code.h | 9 +++++++++ 3 files changed, 26 insertions(+) create mode 100644 code_examples/views/iota.cpp create mode 100644 code_examples/views/iota_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index c8eb7c0..dc70a2a 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -49,3 +49,7 @@ # Doesn't compile with GCC 11 #add_executable(single_view single.cpp) #install(TARGETS single_view DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(iota_view iota.cpp) +#install(TARGETS iota_view DESTINATION bin) diff --git a/code_examples/views/iota.cpp b/code_examples/views/iota.cpp new file mode 100644 index 0000000..387e843 --- /dev/null +++ b/code_examples/views/iota.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include + +int main() { +#include "iota_code.h" + auto cmp1 = {2, 3, 4}; + assert(std::ranges::equal(cmp1, out1)); + auto cmp2 = {42, 43, 44, 45, 46}; + assert(std::ranges::equal(cmp2, out2)); +} diff --git a/code_examples/views/iota_code.h b/code_examples/views/iota_code.h new file mode 100644 index 0000000..3cdab7b --- /dev/null +++ b/code_examples/views/iota_code.h @@ -0,0 +1,9 @@ +std::vector out1; +std::ranges::copy(std::views::iota(2,5), std::back_inserter(out1)); +// finite view [2, 5), out == {2, 3, 4} + +std::vector out2; +std::ranges::copy(std::views::iota(42) | std::views::take(5), +std::back_inserter(out2)); +// infinite view starting with 42, take(5) takes the first five elements from this view +// out2 == {42, 43, 44, 45, 46} From 3d5e4abf9551920152321eec79da6836233dd554 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 12:51:33 +0200 Subject: [PATCH 48/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 060f9fc..12215db 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -265,6 +265,13 @@ \subsection{\texorpdfstring{\cpp{std::views::iota}}{\texttt{std::views::iota}}} The iota view represents a generated sequence formed by repeatedly incrementing an initial value. +\begin{box-note} +\footnotesize Example of using the finite and infinite iota view. +\tcblower +\cppfile{code_examples/views/iota_code.h} +\end{box-note} + + \subsection{\texorpdfstring{\cpp{std::views::istream}}{\texttt{std::views::istream}}} \index{\cpp{std::views::istream}} From 71d698a2d71a66480112d46eeac69d0dbdbc0486 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 13:17:47 +0200 Subject: [PATCH 49/96] Example for istream view. --- code_examples/views/CMakeLists.txt | 4 ++++ code_examples/views/istream.cpp | 9 +++++++++ code_examples/views/istream_code.h | 3 +++ 3 files changed, 16 insertions(+) create mode 100644 code_examples/views/istream.cpp create mode 100644 code_examples/views/istream_code.h diff --git a/code_examples/views/CMakeLists.txt b/code_examples/views/CMakeLists.txt index dc70a2a..1be78a1 100644 --- a/code_examples/views/CMakeLists.txt +++ b/code_examples/views/CMakeLists.txt @@ -53,3 +53,7 @@ # Doesn't compile with GCC 11 #add_executable(iota_view iota.cpp) #install(TARGETS iota_view DESTINATION bin) + +# Doesn't compile with GCC 11 +#add_executable(istream_view istream.cpp) +#install(TARGETS istream_view DESTINATION bin) diff --git a/code_examples/views/istream.cpp b/code_examples/views/istream.cpp new file mode 100644 index 0000000..4f8a1aa --- /dev/null +++ b/code_examples/views/istream.cpp @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include + +int main() { +#include "istream_code.h" +} diff --git a/code_examples/views/istream_code.h b/code_examples/views/istream_code.h new file mode 100644 index 0000000..134407f --- /dev/null +++ b/code_examples/views/istream_code.h @@ -0,0 +1,3 @@ +std::ranges::for_each(std::views::istream(std::cin), [](int v) { + // iterate over integers on standard input +}); From fe661c28a866d2d2474c4f4ee7939487801bc82e Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 13:18:48 +0200 Subject: [PATCH 50/96] Updates from Overleaf --- chapters/04_ranges_in_depth.tex | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 12215db..36cc91b 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -271,9 +271,14 @@ \subsection{\texorpdfstring{\cpp{std::views::iota}}{\texttt{std::views::iota}}} \cppfile{code_examples/views/iota_code.h} \end{box-note} - \subsection{\texorpdfstring{\cpp{std::views::istream}}{\texttt{std::views::istream}}} \index{\cpp{std::views::istream}} The istream view provides similar functionality to istream iterator, except in the form of a view. -It represents a view obtained by successively applying the istream input operator. \ No newline at end of file +It represents a view obtained by successively applying the istream input operator. + +\begin{box-note} +\footnotesize Example of using istream view. +\tcblower +\cppfile{code_examples/views/istream_code.h} +\end{box-note} \ No newline at end of file From ccafda3a8574e9eb248a565bb0a71f1a3fe98fbf Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 13:26:57 +0200 Subject: [PATCH 51/96] Updated book cover. Updated book cover. --- static/book_cover.pdf | Bin 0 -> 67343 bytes static/book_cover.png | Bin 73473 -> 57671 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 static/book_cover.pdf diff --git a/static/book_cover.pdf b/static/book_cover.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2ac5fa069c6a819bb73bd1b5b03e0dd4ff851fbd GIT binary patch literal 67343 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 A=>Px# literal 73473 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 Date: Sat, 30 Jul 2022 13:36:52 +0200 Subject: [PATCH 52/96] Updates from Overleaf --- book.tex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/book.tex b/book.tex index 5277fbd..9f773ce 100644 --- a/book.tex +++ b/book.tex @@ -4,7 +4,7 @@ %\usepackage{showframe} %\checkandfixthelayout -\newcommand{\version}{0.1.1} +\newcommand{\version}{0.2.0} \usepackage{hyperref} \hypersetup{ colorlinks=true, @@ -31,15 +31,15 @@ \begin{document} \frontmatter -%\input{chapters/00_title_page} +\input{chapters/00_title_page} -%\input{chapters/01_preface} % In review +\input{chapters/01_preface} % In review \mainmatter \tableofcontents -%\input{chapters/02_introduction} % In review -%\input{chapters/03_algorithms_00_main} % In review +\input{chapters/02_introduction} % In review +\input{chapters/03_algorithms_00_main} % In review \input{chapters/04_ranges_in_depth} % In review \appendix From 3919ae3042918fbba32224f4115da9237d7de116 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 13:52:36 +0200 Subject: [PATCH 53/96] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 90556a4..7c22451 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,12 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v0.1.1)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.1.1/book_with_cover_v0.1.1.pdf) +[Latest PDF release (v0.2.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.0/book_release_v0.2.0.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.1.1/book_with_cover_v0.1.1.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.0/book_release_v0.2.0.pdf) ## Changelog +- `0.2.0` Added chapter covering C++20 ranges and views. - `0.1.1` Added index and a cover page, small text changes. - `0.1.0` First pre-release From 92fe35b168a1e3aad19fc81018adf3bcfd0a59f1 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 2 Aug 2022 12:41:39 +0200 Subject: [PATCH 54/96] Switch to docker devcontainer. --- .devcontainer/Dockerfile | 8 ++++++++ .devcontainer/devcontainer.json | 8 ++++++++ .vscode/extensions.json | 5 +++++ .vscode/settings.json | 24 ++++++++++++++++++++++++ build/.keep | 1 + code_examples/CMakeLists.txt | 8 ++++---- code_examples/verify.sh | 2 +- packages/code_blocks.tex | 2 +- 8 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 build/.keep diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..ac70e49 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,8 @@ +#FROM ubuntu:22.04 +FROM texlive/texlive:latest + +ARG DEBIAN_FRONTEND=noninteractive + +RUN set -ex && \ + apt-get update && \ + apt-get install -y cmake gcc g++ \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..f2f73c5 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "build": { + "dockerfile": "Dockerfile" + }, + "extensions": [ + "James-Yu.latex-workshop" + ] +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..9c7c13a --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ms-vscode-remote.remote-containers" + ], +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e58fcfb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + "latex-workshop.latex.tools": [ + { + "name": "LuaLaTex", + "command": "lualatex", + "args": [ + "-shell-escape", + "-output-directory=%OUTDIR%", + "%DOC%" + ] + } + ], + "latex-workshop.latex.recipes": [ + { + "name": "Build the book", + "tools": [ + "LuaLaTex" + ] + }, + ], + "latex-workshop.latex.recipe.default": "first", + "latex-workshop.latex.outDir": "build" + +} \ No newline at end of file diff --git a/build/.keep b/build/.keep new file mode 100644 index 0000000..e050474 --- /dev/null +++ b/build/.keep @@ -0,0 +1 @@ +# Dummy file to keep the folder in git. \ No newline at end of file diff --git a/code_examples/CMakeLists.txt b/code_examples/CMakeLists.txt index 2464471..8e37855 100644 --- a/code_examples/CMakeLists.txt +++ b/code_examples/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.5) -project(code_examples LANGUAGES CXX) +set(CMAKE_C_COMPILER "/usr/bin/gcc") +set(CMAKE_CXX_COMPILER "/usr/bin/g++") -set(CMAKE_C_COMPILER "clang-14") -set(CMAKE_CXX_COMPILER "clang++-14") +project(code_examples LANGUAGES CXX) -set(CMAKE_CXX_FLAGS "-std=c++20 -Wall -Wextra -Werror -Wno-unused-variable -pedantic -fsanitize=address,undefined") +set(CMAKE_CXX_FLAGS "-std=c++20 -Wall -Wextra -Werror -Wno-unused-variable -Wno-unused-but-set-variable -pedantic -fsanitize=address,undefined") set(CMAKE_VERBOSE_MAKEFILE OFF) diff --git a/code_examples/verify.sh b/code_examples/verify.sh index d37e6db..a7c3e79 100644 --- a/code_examples/verify.sh +++ b/code_examples/verify.sh @@ -1,5 +1,5 @@ +#!/bin/bash CMAKE="/usr/bin/cmake" - set -e $CMAKE -S. -B build -G "Unix Makefiles" diff --git a/packages/code_blocks.tex b/packages/code_blocks.tex index f60c2e9..88f3745 100644 --- a/packages/code_blocks.tex +++ b/packages/code_blocks.tex @@ -1,4 +1,4 @@ -\usepackage[newfloat=true]{minted} +\usepackage[newfloat=true,outputdir=build]{minted} \usepackage[]{xcolor} \usemintedstyle{xcode} From 71d1fec963e83a5733cd30a87da6b8d3f2a48c84 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 2 Aug 2022 12:52:12 +0200 Subject: [PATCH 55/96] Page numbering fix + docker documentation. Added documentation for building the book from sources. Fixed page numbering issue. --- .gitignore | 1 + README.md | 16 ++++++++++++++++ chapters/00_title_page.tex | 3 ++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ea880d5..07bc41b 100644 --- a/.gitignore +++ b/.gitignore @@ -277,3 +277,4 @@ TSWLatexianTemp* code_examples/bin code_examples/build +build \ No newline at end of file diff --git a/README.md b/README.md index 7c22451..0fbe758 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,19 @@ This repository contains the LaTeX source and C++ code samples for the book "A C - `0.2.0` Added chapter covering C++20 ranges and views. - `0.1.1` Added index and a cover page, small text changes. - `0.1.0` First pre-release + +## Building from sources + +The repository is configured with VSCode devcontainer support. + +Make sure that you have VSCode and Docker installed, then simply open the repository in VSCode. You will be prompted to reopen the project in a docker image. + +VSCode is configured to use the LaTeX Workshop extension. To build the PDF simply press `CTRL+ALT+B` or select `LaTeX Workshop: Build LaTeX Project` from the command palette. +Note that due to the high number of code examples, the build does take a while. +The resulting PDF will be in the build folder. + +### Code samples + +Most code files have wrapping main files that exercise both the build and also contain `assert` expressions that verify correctness of the code. + +The `verify.sh` shell script will build all (except for a few that don't compile with GCC 11) examples, and then run each of them to validate all asserts. \ No newline at end of file diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index 48a45fa..3e0aaf1 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -38,4 +38,5 @@ \endgroup -\clearpage \ No newline at end of file +\clearpage +\pagestyle{plain} \ No newline at end of file From 0b61253e1e7d7519deeb49008f1d598547f5493d Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 2 Aug 2022 13:27:47 +0200 Subject: [PATCH 56/96] Fixed page numbering, small formulation changes. --- book.tex | 2 +- chapters/02_introduction.tex | 10 ++++------ packages/code_blocks.tex | 13 +++++++++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/book.tex b/book.tex index 9f773ce..f5cf9a9 100644 --- a/book.tex +++ b/book.tex @@ -4,7 +4,7 @@ %\usepackage{showframe} %\checkandfixthelayout -\newcommand{\version}{0.2.0} +\newcommand{\version}{0.2.1} \usepackage{hyperref} \hypersetup{ colorlinks=true, diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index b255703..e946031 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -38,7 +38,7 @@ \section{History of standard \texorpdfstring{\CC}{C++} algorithms} \cppfile{code_examples/introduction/history_cc20_code.h} \end{box-note} -As of the time of writing, the \CC23 is not finalized. However, we already know that it will introduce more range algorithms, more views and the ability to implement custom views. +As of the time of writing, the \CC23 standard is not finalized. However, we already know that it will introduce more range algorithms, more views and the ability to implement custom views. \section{Iterators and ranges} @@ -56,7 +56,7 @@ \section{Iterators and ranges} \cppfile{code_examples/introduction/iterators_code.h} \end{box-note} -Sentinels follow the same idea. However, they do not need to be of an iterator type. Instead, they only need to be comparable to an iterator. The end of the range is then the first iterator that compares equal to the sentinel. +Sentinels follow the same idea. However, they do not need to be of an iterator type. Instead, they only need to be comparable to an iterator. The exclusive end of the range is then the first iterator that compares equal to the sentinel. \begin{box-note} \footnotesize Example of specifying a range using an iterator and custom sentinel. The sentinel will compare \cpp{true} with iterators at least the given distance from the start iterator, therefore defining a range with the specified number of elements. @@ -97,9 +97,7 @@ \section{Naming and common behaviour} \subsection{Counted variants "\texttt{\_n}"} -Counted variants of algorithms accept the range specified using the start iterator and the number of elements (instead of begin and end). This behaviour can be a convenient alternative when working with input and output ranges, where we often do not have an explicit end iterator. - -Since \CC20 we also have access to \cpp{std::counted_iterator} adapter, which will only work with the range versions of algorithms.\\ +Counted variants of algorithms accept the range specified using the start iterator and the number of elements (instead of begin and end). This behaviour can be a convenient alternative when working with input and output ranges, where we often do not have an explicit end iterator.\\ \noindent Examples: \cpp{std::for_each_n}, \cpp{std::copy_n}\\\index{\cpp{std::for_each_n}}\index{\cpp{std::copy_n}} @@ -107,7 +105,7 @@ \subsection{Counted variants "\texttt{\_n}"} \subsection{Copy variants "\texttt{\_copy}"} -Copy variants of in-place algorithms that output the result through the provided iterator. The copy behaviour allows this variant to operate on immutable ranges.\\ +Copy variants of in-place algorithms do not write their output back to the source range. Instead, they output the result to one or more output ranges, usually defined by a single iterator denoting the first element to be written to (the number of elements is implied from the source range). The copy behaviour allows these variants to operate on immutable ranges.\\ \noindent Examples: \cpp{std::remove_copy}, \cpp{std::partial_sort_copy}\index{\cpp{std::remove_copy}}\index{\cpp{std::partial_sort_copy}} diff --git a/packages/code_blocks.tex b/packages/code_blocks.tex index 88f3745..dc79bb6 100644 --- a/packages/code_blocks.tex +++ b/packages/code_blocks.tex @@ -1,4 +1,17 @@ +\def\StripPrefix#1>{} +\def\isOverleaf{\fi + \def\overleafJobname{output}% overleaf defaults to 'output' as \jobname + \edef\overleafJobname{\expandafter\StripPrefix\meaning\overleafJobname}% + \edef\job{\jobname}% + \ifx\job\overleafJobname +} + +\if\isOverleaf% +\usepackage[newfloat=true]{minted} +\else \usepackage[newfloat=true,outputdir=build]{minted} +\fi + \usepackage[]{xcolor} \usemintedstyle{xcode} From d9701c009fc76ecf207c01f6e8f2e575fb81fdf8 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 2 Aug 2022 13:32:36 +0200 Subject: [PATCH 57/96] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0fbe758..37b767b 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v0.2.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.0/book_release_v0.2.0.pdf) +[Latest PDF release (v0.2.1)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.1/book_release_v0.2.1.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.0/book_release_v0.2.0.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.1/book_release_v0.2.1.pdf) ## Changelog +- `0.2.1` Fixed page numbering issue, small text changes. - `0.2.0` Added chapter covering C++20 ranges and views. - `0.1.1` Added index and a cover page, small text changes. - `0.1.0` First pre-release @@ -26,4 +27,4 @@ The resulting PDF will be in the build folder. Most code files have wrapping main files that exercise both the build and also contain `assert` expressions that verify correctness of the code. -The `verify.sh` shell script will build all (except for a few that don't compile with GCC 11) examples, and then run each of them to validate all asserts. \ No newline at end of file +The `verify.sh` shell script will build all (except for a few that don't compile with GCC 11) examples, and then run each of them to validate all asserts. From d421c90deae14604d077218a283bda5523fca488 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 17 Aug 2022 10:07:23 +0200 Subject: [PATCH 58/96] Updates from Overleaf --- book.tex | 2 +- chapters/02_introduction.tex | 4 +++- packages/color_blocks.tex | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/book.tex b/book.tex index f5cf9a9..74c444f 100644 --- a/book.tex +++ b/book.tex @@ -11,7 +11,7 @@ linkcolor=blue, filecolor=magenta, urlcolor=cyan, - pdftitle={Overleaf Example}, + pdftitle={Standard C++ Algorithms}, pdfpagemode=FullScreen, } \usepackage{enumitem} diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index e946031..e93b336 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -119,4 +119,6 @@ \subsection{Restrictions on invocable} Many algorithms can be customized using an invocable. However, with a few exceptions, the invocable is not permitted to modify elements of the range or invalidate iterators. On top of that, unless explicitly noted, the algorithms do not guarantee any particular order of invocation. -These restrictions in practice mean that the passed invocable must be regular. The invocable must return the same result if invoked again with the same arguments. This definition permits accessing a global state such as a cache but does not permit invocables that change their result based on their internal state (such as generators). \ No newline at end of file +These restrictions in practice mean that the passed invocable must be regular. The invocable must return the same result if invoked again with the same arguments. This definition permits accessing a global state such as a cache but does not permit invocables that change their result based on their internal state (such as generators). + +\section{A simple mental model for iterators} diff --git a/packages/color_blocks.tex b/packages/color_blocks.tex index af29078..e5200af 100644 --- a/packages/color_blocks.tex +++ b/packages/color_blocks.tex @@ -37,6 +37,7 @@ boxrule = 0pt, toprule = 4.5pt, % top rule weight enhanced, + breakable, drop fuzzy shadow = black!35, fontupper=\sffamily } \ No newline at end of file From b6ce9b2d6e0945cf963d0a6418564d16a26c10a7 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 20:12:49 +0200 Subject: [PATCH 59/96] Example for input ranges. --- code_examples/introduction/CMakeLists.txt | 3 +++ code_examples/introduction/mental_range.cpp | 10 ++++++++++ code_examples/introduction/mental_range_code.h | 7 +++++++ 3 files changed, 20 insertions(+) create mode 100644 code_examples/introduction/mental_range.cpp create mode 100644 code_examples/introduction/mental_range_code.h diff --git a/code_examples/introduction/CMakeLists.txt b/code_examples/introduction/CMakeLists.txt index a7719a9..328ad32 100644 --- a/code_examples/introduction/CMakeLists.txt +++ b/code_examples/introduction/CMakeLists.txt @@ -18,3 +18,6 @@ install(TARGETS sentinels DESTINATION bin) add_executable(categories categories.cpp) install(TARGETS categories DESTINATION bin) + +add_executable(mental_range mental_range.cpp) +install(TARGETS mental_range DESTINATION bin) diff --git a/code_examples/introduction/mental_range.cpp b/code_examples/introduction/mental_range.cpp new file mode 100644 index 0000000..4a92442 --- /dev/null +++ b/code_examples/introduction/mental_range.cpp @@ -0,0 +1,10 @@ +#include +#include +#include +#include + +int main() { +#include "mental_range_code.h" +assert(std::ranges::equal(data, out)); +std::cerr << "."; +} diff --git a/code_examples/introduction/mental_range_code.h b/code_examples/introduction/mental_range_code.h new file mode 100644 index 0000000..11b6a6a --- /dev/null +++ b/code_examples/introduction/mental_range_code.h @@ -0,0 +1,7 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7}; +std::vector out(7,0); + +std::copy(data.begin(), data.end(), // input range + out.begin() // output range, end iterator is implied: + // std::next(out.begin(), std::distance(data.begin(), data.end())); +); From 9899c8a55d0bcf90ad71ad5e3d3a9036b1f363e8 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 20:15:04 +0200 Subject: [PATCH 60/96] Updates from Overleaf --- book.tex | 4 +- chapters/01_preface.tex | 6 +- chapters/02_introduction.tex | 22 ++++-- chapters/03_algorithms_14_uninitialized.tex | 2 +- chapters/03_algorithms_17_minmax.tex | 3 +- chapters/04_ranges_in_depth.tex | 2 +- packages/code_blocks.tex | 8 ++- packages/color_blocks.tex | 36 ++-------- packages/local.tex | 12 ++-- packages/slashbox.sty | 77 --------------------- 10 files changed, 42 insertions(+), 130 deletions(-) delete mode 100644 packages/slashbox.sty diff --git a/book.tex b/book.tex index 74c444f..9995572 100644 --- a/book.tex +++ b/book.tex @@ -4,7 +4,7 @@ %\usepackage{showframe} %\checkandfixthelayout -\newcommand{\version}{0.2.1} +\newcommand{\version}{0.3.0} \usepackage{hyperref} \hypersetup{ colorlinks=true, @@ -33,7 +33,7 @@ \frontmatter \input{chapters/00_title_page} -\input{chapters/01_preface} % In review +\input{chapters/01_preface} % Reviewed \mainmatter \tableofcontents diff --git a/chapters/01_preface.tex b/chapters/01_preface.tex index 8975fce..b273196 100644 --- a/chapters/01_preface.tex +++ b/chapters/01_preface.tex @@ -4,17 +4,17 @@ \chapter{Preface} \section*{About this book} -This book is a complete guide to the \CC standard algorithms. However, that might not mean much to you, so let me unpack this statement. +This book is a complete guide to the \CC\,standard algorithms. However, that might not mean much to you, so let me unpack this statement. This book is a guide, as opposed to a reference, meaning that instead of describing every detail, the book concentrates on examples and pointing out notable, surprising, dangerous or interesting aspects of the different algorithms. Furthermore, unlike a reference, it is supposed to be read, for the most part, like a book in sequential order. -\CC already has one canonical reference, the \CC standard, and for quick lookup, the \href{https://cppreference.com}{cppreference.com} wiki is a great source. +\CC\,already has one canonical reference, the \CC\,standard, and for quick lookup, the \href{https://cppreference.com}{cppreference.com} wiki is a great source. The "complete" part of the statement refers to the width of coverage. The book covers all algorithms and relevant theory up to the \CC20 standard (the \CC23 standard is not finalized at the time of writing). All information is present only in sufficient depth required by the context. This depth limitation keeps the book's overall size reasonable and within the "guide" style. \section*{About the author} -I am Šimon Tóth, the sole author of this book. My primary qualification is 20 years of \CC experience, with approximately 15 of those years \CC being my primary language in professional settings. +I am Šimon Tóth, the sole author of this book. My primary qualification is 20 years of \CC\,experience, with approximately 15 of those years \CC\,being my primary language in professional settings. My background is HPC, spanning academia, big tech and startup environments. I have architected, built and operated systems of all scales, from single machine hardware supported high-availability to planet-scale services.\footnote{You can check my \href{https://cz.linkedin.com/in/simontoth}{LinkedIn profile} for a detailed view of my past career.} diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index e93b336..95e6764 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -1,12 +1,12 @@ \chapter{Introduction} -The \CC standard library is arguably quite limited in its functionality. However, when it comes to data and number crunching, the \CC standard library provides a versatile toolkit of algorithms. +The \CC\,standard library is arguably quite limited in its functionality. However, when it comes to data and number crunching, the \CC\,standard library provides a versatile toolkit of algorithms. -If you are a \CC developer, good familiarity with \CC standard algorithms can save you a lot of effort and accidental bugs. Notably, whenever you see a raw loop in your code, you should question whether calling a standard algorithm wouldn't be a better solution (it usually is). +If you are a \CC\,developer, good familiarity with \CC\,standard algorithms can save you a lot of effort and accidental bugs. Notably, whenever you see a raw loop in your code, you should question whether calling a standard algorithm wouldn't be a better solution (it usually is). \section{History of standard \texorpdfstring{\CC}{C++} algorithms} -While each \CC standard introduced new algorithms or variants, there are few notable milestones in the history of \CC standard algorithms. +While each \CC\,standard introduced new algorithms or variants, there are few notable milestones in the history of \CC\,standard algorithms. The \CC98 standard introduced most of the algorithms. However, it was the \CC11 standard with its introduction of lambdas that made algorithms worthwhile. Before lambdas, the time investment of writing a custom function object made the usefulness of algorithms dubious. @@ -44,7 +44,7 @@ \section{Iterators and ranges} Algorithms operate on data structures, which poses an issue. How do you abstract the implementation details of a specific data structure and allow the algorithm to work with any data structure that satisfies the algorithm's requirements? -The \CC standard library solution to this problem are iterators and ranges. Iterators encapsulate implementation details of data structure traversal and simultaneously expose a set of operations possible on the given data structure in constant time and space. +The \CC\, standard library solution to this problem are iterators and ranges. Iterators encapsulate implementation details of data structure traversal and simultaneously expose a set of operations possible on the given data structure in constant time and space. A range is then denoted by a pair of iterators, or more generally, since \CC20, an iterator and a sentinel. In mathematical terms, a pair of iterators \cpp{it1}, \cpp{it2} denotes a range \cpp{[it1, it2)}, that is, the range includes the element referenced by \cpp{it1} and ends before the element referenced by \cpp{it2}. @@ -121,4 +121,16 @@ \subsection{Restrictions on invocable} These restrictions in practice mean that the passed invocable must be regular. The invocable must return the same result if invoked again with the same arguments. This definition permits accessing a global state such as a cache but does not permit invocables that change their result based on their internal state (such as generators). -\section{A simple mental model for iterators} +\section{A simpler mental model for iterators} + +It can be tricky to internalize all the rules associated with iterators when working with standard algorithms. One shorthand that can help is to think in terms of ranges instead of iterators. + +Ranges passed in as arguments are usually apparent, typically specified by pair of iterators. + + + +The less obvious can be the ranges returned by the algorithms. In some cases, the range is evident from the algorithm. + +In some cases, a single returned iterator denotes multiple ranges. + +Even when the algorithms seem to return an iterator to a specific element, we can still think of the output as a range. \ No newline at end of file diff --git a/chapters/03_algorithms_14_uninitialized.tex b/chapters/03_algorithms_14_uninitialized.tex index f517069..3869cb4 100644 --- a/chapters/03_algorithms_14_uninitialized.tex +++ b/chapters/03_algorithms_14_uninitialized.tex @@ -21,7 +21,7 @@ \subsection{\texorpdfstring{\cpp{std::construct_at}, \cpp{std::destroy_at}}{\tex \cppfile{code_examples/algorithms/create_at_code.h} \end{box-note} -\subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\cpp{std::uninitialized_value_construct},\newline\cpp{std::uninitialized_fill}, \newline\cpp{std::destroy}}{\texttt{std::uninitialized\_default\_construct},\newline\texttt{std::uninitialized_value_construct},\newline\texttt{std::uninitialized_fill}, \newline\texttt{std::destroy}}} +\subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\cpp{std::uninitialized_value_construct},\newline\cpp{std::uninitialized_fill}, \newline\cpp{std::destroy}}{\texttt{std::uninitialized\_default\_construct},\newline\texttt{std::uninitialized\_value\_construct},\newline\texttt{std::uninitialized\_fill}, \newline\texttt{std::destroy}}} \index{\cpp{std::uninitialized_default_construct}} \index{\cpp{std::uninitialized_value_construct}} \index{\cpp{std::uninitialized_fill}} diff --git a/chapters/03_algorithms_17_minmax.tex b/chapters/03_algorithms_17_minmax.tex index fc6bcb9..176b044 100644 --- a/chapters/03_algorithms_17_minmax.tex +++ b/chapters/03_algorithms_17_minmax.tex @@ -77,7 +77,8 @@ \subsection{\texorpdfstring{\cpp{std::clamp}}{\texttt{std::clamp}}} \cppfile{code_examples/algorithms/clamp_code.h} \end{box-note} -\subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \cpp{std::minmax_element}}{\texttt{std::min\_element}, \texttt{std::max\_element}, \texttt{std::minmax\_element}}} +\subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \newline\cpp{std::minmax_element}}{\texttt{std::min\_element}, \texttt{std::max\_element}, +\textCR\texttt{std::minmax\_element}}} \index{\cpp{std::min_element}} \index{\cpp{std::max_element}} \index{\cpp{std::minmax_element}} diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 36cc91b..c99f13b 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -225,7 +225,7 @@ \subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} \cppfile{code_examples/views/all_code.h} \end{box-note} -\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}, \texttt{std::views::join\_view}}} +\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \newline\cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}, \textCR\texttt{std::views::join\_view}}} \index{\cpp{std::views::split}} \index{\cpp{std::views::lazy_split}} \index{\cpp{std::views::join_view}} diff --git a/packages/code_blocks.tex b/packages/code_blocks.tex index dc79bb6..972f694 100644 --- a/packages/code_blocks.tex +++ b/packages/code_blocks.tex @@ -1,3 +1,4 @@ +% Overleaf botch. Normally we use minted files in the build folder, but Overleaf needs special care. \def\StripPrefix#1>{} \def\isOverleaf{\fi \def\overleafJobname{output}% overleaf defaults to 'output' as \jobname @@ -19,6 +20,9 @@ \newminted{cpp}{ linenos=true, + firstnumber=1, + tabsize=4, + obeytabs, xleftmargin=1em, breaklines, fontsize=\footnotesize, @@ -34,4 +38,6 @@ breaklines, fontsize=\footnotesize, numbersep=0.75em -} \ No newline at end of file +} + +\newmintinline[cpp]{cpp}{} \ No newline at end of file diff --git a/packages/color_blocks.tex b/packages/color_blocks.tex index e5200af..de71fdf 100644 --- a/packages/color_blocks.tex +++ b/packages/color_blocks.tex @@ -1,36 +1,4 @@ \usepackage[many]{tcolorbox} % for COLORED BOXES (tikz and xcolor included) -\usepackage{xcolor} - -\tcbset{ - sharp corners, - colback = white, - before skip = 0.2cm, % add extra space before the box - after skip = 0.5cm % add extra space after the box -} % setting global options for tcolorbox - -\newtcolorbox{box-info}{ - colback = LightSkyBlue!20, - colframe = MidnightBlue!50, - boxrule = 0pt, - leftrule = 6pt, % left rule weight - fontupper=\sffamily -} - -\newtcolorbox{box-warning}{ - colback = LightGoldenrodYellow, - colframe = LightGoldenrod, - boxrule = 0pt, - leftrule = 6pt, % left rule weight - fontupper=\sffamily -} - -\newtcolorbox{box-critical}{ - colback = LightSalmon!20, - colframe = OrangeRed!80, - boxrule = 0pt, - leftrule = 6pt, % left rule weight - fontupper=\sffamily -} \newtcolorbox{box-note}{ sharpish corners, % better drop shadow @@ -38,6 +6,10 @@ toprule = 4.5pt, % top rule weight enhanced, breakable, + enlarge top by=5pt, + colback = white, + before skip = 0.2cm, % add extra space before the box + after skip = 0.5cm, % add extra space after the box drop fuzzy shadow = black!35, fontupper=\sffamily } \ No newline at end of file diff --git a/packages/local.tex b/packages/local.tex index 4de713d..76a389e 100644 --- a/packages/local.tex +++ b/packages/local.tex @@ -1,5 +1,6 @@ -\usepackage{sidenotes} -\usepackage{multirow} +\usepackage{sidenotes} % Required +\usepackage{multirow} % Required +\usepackage{diagbox} % Used for boolean algorithms. % Command to consistently format side-panel with C++ versions that introduced different variants of the algorithm. \newcommand{\cppversions}[5]{ @@ -19,6 +20,7 @@ \end{margintable} } +% Command to consistently format the algorithm info table. \newcommand{\constraints}[4]{ \begin{center} \footnotesize @@ -45,8 +47,4 @@ \hline \end{tabular} \end{center} -} - -\newmintinline[cpp]{cpp}{} - -\usepackage{diagbox} +} \ No newline at end of file diff --git a/packages/slashbox.sty b/packages/slashbox.sty deleted file mode 100644 index 7c273f5..0000000 --- a/packages/slashbox.sty +++ /dev/null @@ -1,77 +0,0 @@ -% slashbox.sty by Koichi Yasuoka, May 27, 1993 -% minor modification by Toru Sato, May 31, 1993 -\typeout{slashbox style by K.Yasuoka, May 1993.}% -\newbox\@slashboxa -\newbox\@slashboxb -\newbox\@slashboxc -\newcount\@slashboxwd -\newcount\@slashboxht -\newdimen\@slashsepl -\newdimen\@slashsepr -\def\slashbox{% - \def\@slashboxpicture##1{% - \put(0,0){\line(##1,1){\@slashboxwd}}% - \put(0,\@slashboxht){\makebox(0,0)[tl]{\box\@slashboxa}}% - \put(\@slashboxwd,0){\makebox(0,0)[br]{\box\@slashboxb}}% - }% - \@slashbox -}% -\def\backslashbox{% - \def\@slashboxpicture##1{% - \put(0,\@slashboxht){\line(##1,-1){\@slashboxwd}}% - \put(0,0){\makebox(0,0)[bl]{\box\@slashboxa}}% - \put(\@slashboxwd,\@slashboxht){\makebox(0,0)[tr]{\box\@slashboxb}}% - }% - \@slashbox -}% -\def\@slashbox{\@ifnextchar [{\@@slashbox}{\@@slashbox[0pt]}} -\def\@@slashbox[#1]{\@ifnextchar [{\@@@slashbox[#1]}{\@@@slashbox[#1][c]}} -\def\@@@slashbox[#1][#2]#3#4{% -% #1: width, #2: suppression of \tabcolsep on `l', `r', or `lr' side -% #3: left item, #4: right item - \@slashsepl=\tabcolsep - \@slashsepr=\tabcolsep - \@tfor\@tempa :=#2\do{\expandafter\let - \csname @slashsep\@tempa\endcsname=\z@}% - \setbox\@slashboxa=\hbox{\strut\hskip\tabcolsep\shortstack[l]{#3}}% - \setbox\@slashboxb=\hbox{\shortstack[r]{#4}\hskip\tabcolsep\strut}% - \setbox\@slashboxa=\hbox{\raise\dp\@slashboxa\box\@slashboxa}% - \setbox\@slashboxb=\hbox{\raise\dp\@slashboxb\box\@slashboxb}% - \setbox\@slashboxc=\hbox{% - \@tempdima=\wd\@slashboxa - \advance\@tempdima by \wd\@slashboxb - \advance\@tempdima by \@slashsepl - \advance\@tempdima by \@slashsepr - \@tempdimb=#1\relax% - \ifdim\@tempdimb>\@tempdima \@tempdima=\@tempdimb\fi% - \@tempdimb=\ht\@slashboxa - \advance\@tempdimb by \dp\@slashboxa - \advance\@tempdimb by \ht\@slashboxb - \advance\@tempdimb by \dp\@slashboxb - \@tempcnta=\@tempdima - \@tempcntb=\@tempdimb - \advance\@tempcnta by \@tempcntb - \advance\@tempcnta by -1 - \divide\@tempcnta by \@tempcntb - \ifnum\@tempcnta>6 \@tempcnta=6 - \@tempdimb=0.166666666\@tempdima - \else - \ifnum\@tempcnta<1 \@tempcnta=1\fi - \@tempdima=\@tempdimb - \multiply\@tempdima by \@tempcnta - \fi% - \advance\@tempdima by -\@slashsepl - \advance\@tempdima by -\@slashsepr - \@slashboxwd=\@tempdima - \@slashboxht=\@tempdimb - \@tempcntb=\@slashsepl - \setlength{\unitlength}{1sp}% - \begin{picture}(\@slashboxwd,\@slashboxht)(\@tempcntb,0) - \advance\@tempdima by \@slashsepl - \advance\@tempdima by \@slashsepr - \@slashboxwd=\@tempdima - \@slashboxpicture{\@tempcnta} - \end{picture}% - }% - $\vcenter{\box\@slashboxc}$% -}% \ No newline at end of file From 484e9fedf34e6652fe7053ca35dcd975e3261d9c Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 21:48:11 +0200 Subject: [PATCH 61/96] Example of is_sorted_until. --- code_examples/introduction/CMakeLists.txt | 3 +++ code_examples/introduction/mental_range_code.h | 3 ++- code_examples/introduction/mental_sorted.cpp | 9 +++++++++ code_examples/introduction/mental_sorted_code.h | 13 +++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 code_examples/introduction/mental_sorted.cpp create mode 100644 code_examples/introduction/mental_sorted_code.h diff --git a/code_examples/introduction/CMakeLists.txt b/code_examples/introduction/CMakeLists.txt index 328ad32..8368160 100644 --- a/code_examples/introduction/CMakeLists.txt +++ b/code_examples/introduction/CMakeLists.txt @@ -21,3 +21,6 @@ install(TARGETS categories DESTINATION bin) add_executable(mental_range mental_range.cpp) install(TARGETS mental_range DESTINATION bin) + +add_executable(mental_sorted mental_sorted.cpp) +# Compile only diff --git a/code_examples/introduction/mental_range_code.h b/code_examples/introduction/mental_range_code.h index 11b6a6a..9abb862 100644 --- a/code_examples/introduction/mental_range_code.h +++ b/code_examples/introduction/mental_range_code.h @@ -3,5 +3,6 @@ std::vector out(7,0); std::copy(data.begin(), data.end(), // input range out.begin() // output range, end iterator is implied: - // std::next(out.begin(), std::distance(data.begin(), data.end())); + // std::next(out.begin(), + // std::distance(data.begin(), data.end())); ); diff --git a/code_examples/introduction/mental_sorted.cpp b/code_examples/introduction/mental_sorted.cpp new file mode 100644 index 0000000..6669769 --- /dev/null +++ b/code_examples/introduction/mental_sorted.cpp @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include + +int main() { +#include "mental_sorted_code.h" +} diff --git a/code_examples/introduction/mental_sorted_code.h b/code_examples/introduction/mental_sorted_code.h new file mode 100644 index 0000000..896dba2 --- /dev/null +++ b/code_examples/introduction/mental_sorted_code.h @@ -0,0 +1,13 @@ +std::vector data{1, 4, 5, 7, 9, 2, 3}; + +// is_sorted_until returns the first out of order element. +auto result = std::is_sorted_until(data.begin(), data.end()); + +// [begin, result) is the maximal sorted sub-range +for (auto it = data.begin(); it != result; it++) { + // Iterate over all elements in the sorted sub-range. + // {1, 4, 5, 7, 9} +} +for (auto v : std::ranges::subrange(data.begin(), result)) { + // Same, but using a range-based for loop. +} From 41a21882ee09e129cf17aa29497b51a6aac556de Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 21:49:30 +0200 Subject: [PATCH 62/96] Updates from Overleaf --- book.tex | 6 +++--- chapters/02_introduction.tex | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/book.tex b/book.tex index 9995572..59ae40f 100644 --- a/book.tex +++ b/book.tex @@ -33,14 +33,14 @@ \frontmatter \input{chapters/00_title_page} -\input{chapters/01_preface} % Reviewed +%\input{chapters/01_preface} % Reviewed \mainmatter \tableofcontents \input{chapters/02_introduction} % In review -\input{chapters/03_algorithms_00_main} % In review -\input{chapters/04_ranges_in_depth} % In review +%\input{chapters/03_algorithms_00_main} % In review +%\input{chapters/04_ranges_in_depth} % In review \appendix \input{chapters/XX_index} diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 95e6764..c93ddc3 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -127,10 +127,16 @@ \section{A simpler mental model for iterators} Ranges passed in as arguments are usually apparent, typically specified by pair of iterators. - +\begin{box-note} +\footnotesize Example with two ranges passed in as an argument. The input range is fully specified, and the end iterator for the output range is implied from the number of elements in the input range. +\tcblower +\cppfile{code_examples/introduction/mental_range_code.h} +\end{box-note} The less obvious can be the ranges returned by the algorithms. In some cases, the range is evident from the algorithm. + + In some cases, a single returned iterator denotes multiple ranges. Even when the algorithms seem to return an iterator to a specific element, we can still think of the output as a range. \ No newline at end of file From 974ad98444b4ce457d4b53f748e84aa8b84a9e71 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 22:19:29 +0200 Subject: [PATCH 63/96] Example with two sub-ranges. --- code_examples/introduction/CMakeLists.txt | 3 +++ code_examples/introduction/mental_two.cpp | 9 +++++++++ code_examples/introduction/mental_two_code.h | 11 +++++++++++ 3 files changed, 23 insertions(+) create mode 100644 code_examples/introduction/mental_two.cpp create mode 100644 code_examples/introduction/mental_two_code.h diff --git a/code_examples/introduction/CMakeLists.txt b/code_examples/introduction/CMakeLists.txt index 8368160..05e8e32 100644 --- a/code_examples/introduction/CMakeLists.txt +++ b/code_examples/introduction/CMakeLists.txt @@ -24,3 +24,6 @@ install(TARGETS mental_range DESTINATION bin) add_executable(mental_sorted mental_sorted.cpp) # Compile only + +add_executable(mental_two mental_two.cpp) +# Compile only diff --git a/code_examples/introduction/mental_two.cpp b/code_examples/introduction/mental_two.cpp new file mode 100644 index 0000000..3985d07 --- /dev/null +++ b/code_examples/introduction/mental_two.cpp @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include + +int main() { +#include "mental_two_code.h" +} diff --git a/code_examples/introduction/mental_two_code.h b/code_examples/introduction/mental_two_code.h new file mode 100644 index 0000000..6bc63d4 --- /dev/null +++ b/code_examples/introduction/mental_two_code.h @@ -0,0 +1,11 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7, 8, 9}; + +// lower_bound returns the first element !(el < 4) +auto lb = std::lower_bound(data.begin(), data.end(), 4); + +for (auto v : std::ranges::subrange(data.begin(), lb)) { + // lower range [begin, lb): elements < 4 +} +for (auto v : std::ranges::subrange(lb, data.end())) { + // upper range [lb, end): elements >= 4 +} From cb4be6e8b96f3ff0c76c617d19dc8dd5a43bab35 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 22:21:24 +0200 Subject: [PATCH 64/96] Updates from Overleaf --- chapters/02_introduction.tex | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index c93ddc3..952dbf6 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -135,8 +135,20 @@ \section{A simpler mental model for iterators} The less obvious can be the ranges returned by the algorithms. In some cases, the range is evident from the algorithm. +\begin{box-note} +\footnotesize Example of \cpp{std::is_sorted_until} that returns an iterator to the first out-of-order element, which can also be thought as the end iterator for a maximal sorted sub-range. +\tcblower +\cppfile{code_examples/introduction/mental_sorted_code.h} +\end{box-note} +The benefit of thinking about the returned value as the end iterator of a range is that it removes the potential for corner cases. For example, what if the algorithm doesn't find any element out of order? The returned value will be the end iterator of the source range, meaning that the range returned is simply the entire source range. In some cases, a single returned iterator denotes multiple ranges. +\begin{box-note} +\footnotesize Example of \cpp{std::lower_bound} that splits the range into two sub-ranges. +\tcblower +\cppfile{code_examples/introduction/mental_two_code.h} +\end{box-note} + Even when the algorithms seem to return an iterator to a specific element, we can still think of the output as a range. \ No newline at end of file From b33661d675c5ef4a5826292b4e14af9cd54f21bb Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 6 Sep 2022 16:26:32 +0200 Subject: [PATCH 65/96] Example with find. --- code_examples/introduction/CMakeLists.txt | 3 +++ code_examples/introduction/mental_find.cpp | 6 ++++++ code_examples/introduction/mental_find_code.h | 10 ++++++++++ 3 files changed, 19 insertions(+) create mode 100644 code_examples/introduction/mental_find.cpp create mode 100644 code_examples/introduction/mental_find_code.h diff --git a/code_examples/introduction/CMakeLists.txt b/code_examples/introduction/CMakeLists.txt index 05e8e32..6af82e0 100644 --- a/code_examples/introduction/CMakeLists.txt +++ b/code_examples/introduction/CMakeLists.txt @@ -27,3 +27,6 @@ add_executable(mental_sorted mental_sorted.cpp) add_executable(mental_two mental_two.cpp) # Compile only + +add_executable(mental_find mental_find.cpp) +# Compile only diff --git a/code_examples/introduction/mental_find.cpp b/code_examples/introduction/mental_find.cpp new file mode 100644 index 0000000..be40475 --- /dev/null +++ b/code_examples/introduction/mental_find.cpp @@ -0,0 +1,6 @@ +#include +#include + +int main() { +#include "mental_find_code.h" +} diff --git a/code_examples/introduction/mental_find_code.h b/code_examples/introduction/mental_find_code.h new file mode 100644 index 0000000..0fa87e7 --- /dev/null +++ b/code_examples/introduction/mental_find_code.h @@ -0,0 +1,10 @@ +std::string str("Hello World!"); + +// Returns the iterator to the first occurence of ' ' +auto it = std::find(str.begin(), str.end(), ' '); + +// Range [begin, it) is the maximal prefix range +// that doesn't contain ' ' +for (auto v : std::string_view(str.begin(), it)) { + // iterate over "Hello" +} From f28fac5c6ec5694684ba8a24497919291d606efa Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 6 Sep 2022 16:27:51 +0200 Subject: [PATCH 66/96] Updates from Overleaf --- chapters/02_introduction.tex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 952dbf6..a32d8f9 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -133,7 +133,7 @@ \section{A simpler mental model for iterators} \cppfile{code_examples/introduction/mental_range_code.h} \end{box-note} -The less obvious can be the ranges returned by the algorithms. In some cases, the range is evident from the algorithm. +The returned range can also be evident from the semantics of the algorithm. \begin{box-note} \footnotesize Example of \cpp{std::is_sorted_until} that returns an iterator to the first out-of-order element, which can also be thought as the end iterator for a maximal sorted sub-range. @@ -151,4 +151,5 @@ \section{A simpler mental model for iterators} \cppfile{code_examples/introduction/mental_two_code.h} \end{box-note} -Even when the algorithms seem to return an iterator to a specific element, we can still think of the output as a range. \ No newline at end of file +Even when the algorithm returns an iterator to a specific element, it might be worth considering the implied range. + From 981ae109215bbe4efc2b2831b0e76ac44cd2f041 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 19 Sep 2022 10:04:12 +0200 Subject: [PATCH 67/96] Unqualified lookup example. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_unqalified.cpp | 2 ++ code_examples/theory/adl_unqalified_code.h | 11 +++++++++++ 3 files changed, 16 insertions(+) create mode 100644 code_examples/theory/adl_unqalified.cpp create mode 100644 code_examples/theory/adl_unqalified_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index c70b848..19beec4 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -12,3 +12,6 @@ install(TARGETS const_cast DESTINATION bin) add_executable(initializer_list initializer_list.cpp) install(TARGETS initializer_list DESTINATION bin) + +add_executable(adl_unqalified adl_unqalified.cpp) +# Compile only diff --git a/code_examples/theory/adl_unqalified.cpp b/code_examples/theory/adl_unqalified.cpp new file mode 100644 index 0000000..dde1059 --- /dev/null +++ b/code_examples/theory/adl_unqalified.cpp @@ -0,0 +1,2 @@ +#include "adl_unqalified_code.h" +int main() {} diff --git a/code_examples/theory/adl_unqalified_code.h b/code_examples/theory/adl_unqalified_code.h new file mode 100644 index 0000000..d13b7fd --- /dev/null +++ b/code_examples/theory/adl_unqalified_code.h @@ -0,0 +1,11 @@ +namespace A { +void some_call(int) {} +namespace B { +void some_call(double) {} + +void test() { + some_call(1); // A::B::some_call + some_call(2.0); // A::B::some_call +} +} +} From 66863c68fbd050624e9d1596c0821f5f85b5c6b3 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 19 Sep 2022 10:11:13 +0200 Subject: [PATCH 68/96] Updates from Overleaf --- book.tex | 6 +++++- chapters/02_introduction.tex | 10 ++++++++-- chapters/03_algorithms_01_foreach.tex | 8 ++++---- chapters/90_theory.tex | 13 +++++++++++++ packages/color_blocks.tex | 13 +++++++++++++ 5 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 chapters/90_theory.tex diff --git a/book.tex b/book.tex index 59ae40f..bff00c6 100644 --- a/book.tex +++ b/book.tex @@ -38,13 +38,17 @@ \mainmatter \tableofcontents -\input{chapters/02_introduction} % In review +%\input{chapters/02_introduction} % In review %\input{chapters/03_algorithms_00_main} % In review %\input{chapters/04_ranges_in_depth} % In review +\input{chapters/90_theory} % TODO + \appendix \input{chapters/XX_index} +% Section on iterator invalidation + \backmatter \end{document} \ No newline at end of file diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index a32d8f9..70941f1 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -10,11 +10,12 @@ \section{History of standard \texorpdfstring{\CC}{C++} algorithms} The \CC98 standard introduced most of the algorithms. However, it was the \CC11 standard with its introduction of lambdas that made algorithms worthwhile. Before lambdas, the time investment of writing a custom function object made the usefulness of algorithms dubious. -\begin{box-note} +\raggedbottom +\begin{box-nobreak} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm with a custom function object, calculating the number of elements and their sum. \tcblower \cppfile{code_examples/introduction/history_cc98_code.h} -\end{box-note} +\end{box-nobreak} \begin{box-note} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm with a capturing lambda, calculating the number of elements and their sum. @@ -153,3 +154,8 @@ \section{A simpler mental model for iterators} Even when the algorithm returns an iterator to a specific element, it might be worth considering the implied range. +\begin{box-note} +\footnotesize Example of \cpp{std::find} establishing a prefix range that doesn't contain the searched-for element. +\tcblower +\cppfile{code_examples/introduction/mental_find_code.h} +\end{box-note} \ No newline at end of file diff --git a/chapters/03_algorithms_01_foreach.tex b/chapters/03_algorithms_01_foreach.tex index f068ace..2acd028 100644 --- a/chapters/03_algorithms_01_foreach.tex +++ b/chapters/03_algorithms_01_foreach.tex @@ -7,7 +7,7 @@ \section{Introducing the algorithms} \begin{enumerate}[label=\protect\circled{\arabic*}] \item The presentation of each algorithm will start with a short description. \item The margin will contain information about the history of the algorithm: which C++ standard introduced it and whether it has constexpr, parallel and range variants and including versions of the standard that introduced them. - \item Following that will be the description of constraints. Algorithms that write data to a distinct output range are denoted with an arrow: \cpp{input_range -> output_range}. + \item Following that will be the description of constraints. Algorithms that write data to a distinct output range are denoted with an arrow: \newline\cpp{input_range -> output_range}. \item Finally, each description will conclude with one or more examples with explanations. \end{enumerate} @@ -36,7 +36,7 @@ \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} \end{tabular} \end{center} -\circled{4} The C++11 standard introduced the range loop, which mostly replaced the uses of \cpp{std::for_each}. +\circled{4} The C++11 standard introduced the range-based for loop, which mostly replaced the uses of \cpp{std::for_each}. \begin{box-note} \footnotesize Example of a range loop over all elements of a \cpp{std::vector}. @@ -52,7 +52,7 @@ \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} However, there are still a few corner cases when using \cpp{std::for_each} is preferable. -The first case is straightforward parallelization. Invoking an expensive operation for each element in parallel is trivial with \cpp{std::for_each}. On top of that, as long as the operations are independent, there is no need for synchronization primitives. +The first case is straightforward parallelization. Invoking an expensive operation for each element in parallel is trivial with \cpp{std::for_each}. As long as the operations are independent, there is no need for synchronization primitives. \begin{box-note} \footnotesize Example of a parallel \cpp{std::for_each} invoking a method on each element independently in parallel. @@ -63,7 +63,7 @@ \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} Second, the range version can provide a more concise and explicit syntax in some cases because of the projection support introduced in C++20. \begin{box-note} -\footnotesize Example of the range version \cpp{std::ranges::for_each} utilizing a projection to iterate over the result of invoking the method \cpp{getValue()} on each element. +\footnotesize Example of the range version of \cpp{std::ranges::for_each} utilizing a projection to invoke the method \cpp{getValue()} (line 13) on each element and summing the resulting values using a lambda (line 12). \tcblower \cppfile{code_examples/algorithms/for_each_code_cpp20.h} \end{box-note} diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex new file mode 100644 index 0000000..91a3382 --- /dev/null +++ b/chapters/90_theory.tex @@ -0,0 +1,13 @@ +\chapter{In-depth} + +In this chapter, we will go over relevant in-depth topics referenced throughout the rest of the book. + +\section{Argument-dependent lookup (ADL)} + +When calling a method without qualification (i.e. not specifying the namespace), the compiler needs to determine the set of candidate functions. As a first step, the compiler will do an unqualified name lookup, which starts at the local scope and examines the parent scopes until it finds the first instance of the name (at which point it stops). + +\begin{box-note} +\footnotesize Example of unqualified lookup. Both calls to \cpp{some_call} will resolve to \cpp{::A::B::some_call} since this is the first instance discovered by the compiler. +\tcblower +\cppfile{code_examples/theory/adl_unqualified.h} +\end{box-note} \ No newline at end of file diff --git a/packages/color_blocks.tex b/packages/color_blocks.tex index de71fdf..602ca3b 100644 --- a/packages/color_blocks.tex +++ b/packages/color_blocks.tex @@ -12,4 +12,17 @@ after skip = 0.5cm, % add extra space after the box drop fuzzy shadow = black!35, fontupper=\sffamily +} + +\newtcolorbox{box-nobreak}{ + sharpish corners, % better drop shadow + boxrule = 0pt, + toprule = 4.5pt, % top rule weight + enhanced, + enlarge top by=5pt, + colback = white, + before skip = 0.2cm, % add extra space before the box + after skip = 0.5cm, % add extra space after the box + drop fuzzy shadow = black!35, + fontupper=\sffamily } \ No newline at end of file From 98f84e98fc2ecfe73594e59574a66bdab6641c54 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 20 Sep 2022 15:29:11 +0200 Subject: [PATCH 69/96] Simple ADL example. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_code.h | 2 +- code_examples/theory/adl_simple.cpp | 5 +++++ code_examples/theory/adl_simple_code.h | 17 +++++++++++++++++ code_examples/theory/adl_unqalified.cpp | 4 +++- code_examples/theory/adl_unqalified_code.h | 4 +++- 6 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 code_examples/theory/adl_simple.cpp create mode 100644 code_examples/theory/adl_simple_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index 19beec4..265d275 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -15,3 +15,6 @@ install(TARGETS initializer_list DESTINATION bin) add_executable(adl_unqalified adl_unqalified.cpp) # Compile only + +add_executable(adl_simple adl_simple.cpp) +# Compile only diff --git a/code_examples/theory/adl_code.h b/code_examples/theory/adl_code.h index 5f03abc..51de88b 100644 --- a/code_examples/theory/adl_code.h +++ b/code_examples/theory/adl_code.h @@ -10,5 +10,5 @@ std::ostream& operator << (std::ostream& s, const X&) { void calling_site() { Custom::X x; Custom::operator << (std::cout, x); // OK, explicit call - std::cout << x; // OK, unqualified operator <<, ADL + std::cout << x; // Requires ADL } diff --git a/code_examples/theory/adl_simple.cpp b/code_examples/theory/adl_simple.cpp new file mode 100644 index 0000000..e35667d --- /dev/null +++ b/code_examples/theory/adl_simple.cpp @@ -0,0 +1,5 @@ +#include +#include "adl_simple_code.h" +int main() { + calling_site(); +} diff --git a/code_examples/theory/adl_simple_code.h b/code_examples/theory/adl_simple_code.h new file mode 100644 index 0000000..f9400b8 --- /dev/null +++ b/code_examples/theory/adl_simple_code.h @@ -0,0 +1,17 @@ +namespace A { + struct X {}; + void some_func(const X&) {} +} + +namespace B { + struct Y {}; + void some_func(const Y&) {} +} + +void some_func(const auto&) {} + +void calling_site() { + A::X x; B::Y y; + some_func(x); // Calls A::some_func + some_func(y); // Calls B::some_func +} diff --git a/code_examples/theory/adl_unqalified.cpp b/code_examples/theory/adl_unqalified.cpp index dde1059..82a0145 100644 --- a/code_examples/theory/adl_unqalified.cpp +++ b/code_examples/theory/adl_unqalified.cpp @@ -1,2 +1,4 @@ #include "adl_unqalified_code.h" -int main() {} +int main() { + A::B::calling_site(); +} diff --git a/code_examples/theory/adl_unqalified_code.h b/code_examples/theory/adl_unqalified_code.h index d13b7fd..93df3bd 100644 --- a/code_examples/theory/adl_unqalified_code.h +++ b/code_examples/theory/adl_unqalified_code.h @@ -1,11 +1,13 @@ namespace A { void some_call(int) {} +void some_call(const char*) {} namespace B { void some_call(double) {} -void test() { +void calling_site() { some_call(1); // A::B::some_call some_call(2.0); // A::B::some_call + //some_call("hello world"); Will not compile, no conversion from const char* -> double } } } From 3c9eb61f3e75e3abb91a3ad19e5991859aeae723 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 20 Sep 2022 15:30:48 +0200 Subject: [PATCH 70/96] Updates from Overleaf --- chapters/90_theory.tex | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 91a3382..7065569 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -9,5 +9,21 @@ \section{Argument-dependent lookup (ADL)} \begin{box-note} \footnotesize Example of unqualified lookup. Both calls to \cpp{some_call} will resolve to \cpp{::A::B::some_call} since this is the first instance discovered by the compiler. \tcblower -\cppfile{code_examples/theory/adl_unqualified.h} +\cppfile{code_examples/theory/adl_unqalified_code.h} +\end{box-note} + +Due to the simplicity of unqualified lookup, we need an additional mechanism to discover overloads. Notably, it is a requirement for operator overloading since operator calls are unqualified. This is where argument-dependent lookup comes in. + +\begin{box-note} +\footnotesize Without ADL, any call to a custom operator overload would have to be fully qualified, requiring the function call syntax. +\tcblower +\cppfile{code_examples/theory/adl_code.h} +\end{box-note} + +While the full rules for ADL are long, the heavily simplified version is that the compiler will also consider the innermost namespace of all the arguments when determining the viable function overloads. + +\begin{box-note} +\footnotesize +\tcblower +\cppfile{code_examples/theory/adl_code.h} \end{box-note} \ No newline at end of file From 608397d06741151dfa3dcbbbb90e138e7c079dfc Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 27 Sep 2022 18:38:01 +0200 Subject: [PATCH 71/96] Example of non-function symbol. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_nonfunc.cpp | 5 +++++ code_examples/theory/adl_nonfunc_code.h | 11 +++++++++++ code_examples/theory/adl_unqalified_code.h | 3 ++- 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 code_examples/theory/adl_nonfunc.cpp create mode 100644 code_examples/theory/adl_nonfunc_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index 265d275..3771446 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -18,3 +18,6 @@ add_executable(adl_unqalified adl_unqalified.cpp) add_executable(adl_simple adl_simple.cpp) # Compile only + +add_executable(adl_nonfunc adl_nonfunc.cpp) +# Compile only diff --git a/code_examples/theory/adl_nonfunc.cpp b/code_examples/theory/adl_nonfunc.cpp new file mode 100644 index 0000000..8643d7b --- /dev/null +++ b/code_examples/theory/adl_nonfunc.cpp @@ -0,0 +1,5 @@ +#include "adl_nonfunc_code.h" + +int main() { + calling_site(); +} diff --git a/code_examples/theory/adl_nonfunc_code.h b/code_examples/theory/adl_nonfunc_code.h new file mode 100644 index 0000000..3879cec --- /dev/null +++ b/code_examples/theory/adl_nonfunc_code.h @@ -0,0 +1,11 @@ +namespace Custom { + struct X {}; + + constexpr inline auto some_func = [](const X&) {}; +} + +void calling_site() { + Custom::X x; + // some_func(x); // Will not compile, not visible to ADL. + Custom::some_func(x); // OK +} diff --git a/code_examples/theory/adl_unqalified_code.h b/code_examples/theory/adl_unqalified_code.h index 93df3bd..e136be0 100644 --- a/code_examples/theory/adl_unqalified_code.h +++ b/code_examples/theory/adl_unqalified_code.h @@ -7,7 +7,8 @@ void some_call(double) {} void calling_site() { some_call(1); // A::B::some_call some_call(2.0); // A::B::some_call - //some_call("hello world"); Will not compile, no conversion from const char* -> double + // some_call("hello world"); will not compile + // no conversion from const char* -> double } } } From edfd4934aa87d6be0fdd619cd5e3854cb6bc9bf3 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 27 Sep 2022 18:38:24 +0200 Subject: [PATCH 72/96] Updates from Overleaf --- chapters/90_theory.tex | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 7065569..f25acb5 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -1,6 +1,6 @@ -\chapter{In-depth} +\chapter{Bits of C++ theory} -In this chapter, we will go over relevant in-depth topics referenced throughout the rest of the book. +This chapter will dive deep into the various topics referenced throughout the book. While this chapter serves as a reference, the topics are still presented in a heavily simplified, example-heavy format. For a proper reference, please refer to the C++ standard. \section{Argument-dependent lookup (ADL)} @@ -20,10 +20,25 @@ \section{Argument-dependent lookup (ADL)} \cppfile{code_examples/theory/adl_code.h} \end{box-note} -While the full rules for ADL are long, the heavily simplified version is that the compiler will also consider the innermost namespace of all the arguments when determining the viable function overloads. +While the full rules for ADL are quite complex, the heavily simplified version is that the compiler will also consider the innermost namespace of all the arguments when determining the viable function overloads. \begin{box-note} \footnotesize \tcblower -\cppfile{code_examples/theory/adl_code.h} -\end{box-note} \ No newline at end of file +\cppfile{code_examples/theory/adl_simple_code.h} +\end{box-note} + +Arguably the true power of ADL lies in the interactions with other language features, so let's look at how ADL interacts with friend functions and function objects. + +\subsection{Friend functions vs ADL} + +Friend functions (when defined inline) do not participate in the normal lookup (they are part of the surrounding namespace but are not visible). However, they are still visible to ADL, which permits a common implementation pattern for a default implementation with a customization point through ADL. + +\begin{box-note} +\footnotesize Example demonstrating a default implementation with a customization point through ADL. The default implementation needs to be discovered during the unqualified lookup; therefore, if any custom implementation is visible in the surrounding namespaces, it will block this discovery. +\tcblower +\cppfile{code_examples/theory/adl_default_code.h} +\end{box-note} + +\subsection{Function objects vs ADL} + From 0e94f238791b36b98b2c398122aeea19d1321571 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 27 Sep 2022 19:26:02 +0200 Subject: [PATCH 73/96] Example of shutting down ADL. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_shutdown.cpp | 4 ++++ code_examples/theory/adl_shutdown_code.h | 13 +++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 code_examples/theory/adl_shutdown.cpp create mode 100644 code_examples/theory/adl_shutdown_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index 3771446..1884d01 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -21,3 +21,6 @@ add_executable(adl_simple adl_simple.cpp) add_executable(adl_nonfunc adl_nonfunc.cpp) # Compile only + +add_executable(adl_shutdown adl_shutdown.cpp) +# Compile only diff --git a/code_examples/theory/adl_shutdown.cpp b/code_examples/theory/adl_shutdown.cpp new file mode 100644 index 0000000..a4c4e78 --- /dev/null +++ b/code_examples/theory/adl_shutdown.cpp @@ -0,0 +1,4 @@ +#include "adl_shutdown_code.h" +int main() { + calling_site(); +} diff --git a/code_examples/theory/adl_shutdown_code.h b/code_examples/theory/adl_shutdown_code.h new file mode 100644 index 0000000..4a93428 --- /dev/null +++ b/code_examples/theory/adl_shutdown_code.h @@ -0,0 +1,13 @@ +namespace Custom { + struct X { + friend void some_func(const X&) {} + }; +} + +constexpr inline auto some_func = [](const auto&) {}; + +void calling_site() { + Custom::X x; + some_func(x); // calls ::some_func + // Because ::some_func prevents ADL, Custom::some_func cannot be called. +} From 2c1641be364597b4974ac9ec498322b38fc1c1de Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 27 Sep 2022 19:28:18 +0200 Subject: [PATCH 74/96] Updates from Overleaf --- chapters/90_theory.tex | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index f25acb5..ef15732 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -42,3 +42,20 @@ \subsection{Friend functions vs ADL} \subsection{Function objects vs ADL} +The second notable interaction occurs with non-function symbols. In the context of this section, the important ones are function objects (and lambdas). + +Argument-dependent lookup will not consider non-function symbols. This means that a function object or a lambda must be either visible to an unqualified lookup or need to be fully qualified. + +\begin{box-note} +\footnotesize Example demonstrating a lambda stored in an inline variable that can only be invoked through a qualified call. +\tcblower +\cppfile{code_examples/theory/adl_nonfunc_code.h} +\end{box-note} + +Finally, discovering a non-function symbol during the unqualified lookup phase will prevent ADL completely. + +\begin{box-note} +\footnotesize Example demonstrating a non-function object preventing ADL, making a friend function impossible to invoke. +\tcblower +\cppfile{code_examples/theory/adl_shutdown_code.h} +\end{box-note} \ No newline at end of file From f31788f44b5a35886e92882217408b96acdb2e76 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:34:15 +0200 Subject: [PATCH 75/96] Example for Niebloid. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_niebloid.cpp | 6 +++++ code_examples/theory/adl_niebloid_code.h | 29 ++++++++++++++++++++++++ code_examples/theory/adl_shutdown_code.h | 2 +- 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 code_examples/theory/adl_niebloid.cpp create mode 100644 code_examples/theory/adl_niebloid_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index 1884d01..f1bdbbd 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -24,3 +24,6 @@ add_executable(adl_nonfunc adl_nonfunc.cpp) add_executable(adl_shutdown adl_shutdown.cpp) # Compile only + +add_executable(adl_niebloid adl_niebloid.cpp) +# Compile only diff --git a/code_examples/theory/adl_niebloid.cpp b/code_examples/theory/adl_niebloid.cpp new file mode 100644 index 0000000..9b45688 --- /dev/null +++ b/code_examples/theory/adl_niebloid.cpp @@ -0,0 +1,6 @@ +#include + +#include "adl_niebloid_code.h" +int main() { + calling_site(); +} diff --git a/code_examples/theory/adl_niebloid_code.h b/code_examples/theory/adl_niebloid_code.h new file mode 100644 index 0000000..5552b66 --- /dev/null +++ b/code_examples/theory/adl_niebloid_code.h @@ -0,0 +1,29 @@ +namespace dflt { +namespace impl { + template + concept HasCustomImpl = requires(T a) { do_something(a); }; + + struct DoSomethingFn { + template void operator()(T&& arg) const + requires HasCustomImpl { do_something(std::forward(arg)); } + template void operator()(T&&) const + requires (!HasCustomImpl) { /* default implementation */ } + }; +} + +inline namespace var { + constexpr inline auto do_something = impl::DoSomethingFn{}; +} +} + +namespace custom { + struct X { friend void do_something(const X&){}; }; + struct Y {}; +} + +void calling_site() { + custom::X x; + custom::Y y; + dflt::do_something(x); // calls custom::do_something(const X&) + dflt::do_something(y); // calls default implementation +} diff --git a/code_examples/theory/adl_shutdown_code.h b/code_examples/theory/adl_shutdown_code.h index 4a93428..ef6ac20 100644 --- a/code_examples/theory/adl_shutdown_code.h +++ b/code_examples/theory/adl_shutdown_code.h @@ -9,5 +9,5 @@ constexpr inline auto some_func = [](const auto&) {}; void calling_site() { Custom::X x; some_func(x); // calls ::some_func - // Because ::some_func prevents ADL, Custom::some_func cannot be called. + // Because ADL is skipped, Custom::some_func cannot be called. } From e7e556ca88373c3499ff944bc5dc3c8a79bc5791 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:34:39 +0200 Subject: [PATCH 76/96] Updates from Overleaf --- chapters/00_title_page.tex | 2 ++ chapters/90_theory.tex | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index 3e0aaf1..ce7d96f 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -30,6 +30,8 @@ Original copy of this book can be obtained at \href{https://github.com/HappyCerberus/book-cpp-algorithms}{https://github.com/HappyCerberus/book-cpp-algorithms}. +The book is also available through LeanPub where the proceeds go to Electronic Frontier Foundation (after LeanPub takes their cut) \href{https://leanpub.com/cpp-algorithms-guide}{https://leanpub.com/cpp-algorithms-guide}. + This copy of the book is version \version. \vfill diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index ef15732..186f7f6 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -58,4 +58,10 @@ \subsection{Function objects vs ADL} \footnotesize Example demonstrating a non-function object preventing ADL, making a friend function impossible to invoke. \tcblower \cppfile{code_examples/theory/adl_shutdown_code.h} -\end{box-note} \ No newline at end of file +\end{box-note} + +\subsection{\texorpdfstring{\CC20 ADL customization point}{C++20 ADL customization point}} + +With the introduction of concepts in \CC20, we now have a cleaner way to introduce a customization point using ADL. + +This approach has several benefits, in particular on the call site. We no longer need to remember to pull in the namespace of the default implementation. Furthermore, because the call is now fully qualified, we avoid the problem of symbol collisions that can potentially completely prevent ADL. \ No newline at end of file From a5ab43e6241621790e0296964362e169bb28112a Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:36:21 +0200 Subject: [PATCH 77/96] Formatting fix --- code_examples/theory/adl_niebloid_code.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code_examples/theory/adl_niebloid_code.h b/code_examples/theory/adl_niebloid_code.h index 5552b66..0b90065 100644 --- a/code_examples/theory/adl_niebloid_code.h +++ b/code_examples/theory/adl_niebloid_code.h @@ -5,7 +5,10 @@ namespace impl { struct DoSomethingFn { template void operator()(T&& arg) const - requires HasCustomImpl { do_something(std::forward(arg)); } + requires HasCustomImpl { + do_something(std::forward(arg)); + } + template void operator()(T&&) const requires (!HasCustomImpl) { /* default implementation */ } }; From ac10beb25a485c1f20ab164a4cf5d361769d9b1e Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:36:43 +0200 Subject: [PATCH 78/96] Updates from Overleaf --- chapters/90_theory.tex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 186f7f6..3c31311 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -64,4 +64,10 @@ \subsection{\texorpdfstring{\CC20 ADL customization point}{C++20 ADL customizati With the introduction of concepts in \CC20, we now have a cleaner way to introduce a customization point using ADL. +\begin{box-note} +\footnotesize Example. +\tcblower +\cppfile{code_examples/theory/adl_niebloid_code.h} +\end{box-note} + This approach has several benefits, in particular on the call site. We no longer need to remember to pull in the namespace of the default implementation. Furthermore, because the call is now fully qualified, we avoid the problem of symbol collisions that can potentially completely prevent ADL. \ No newline at end of file From cf6f83fb09d2fc32945e506f8ff402b7f41e623c Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:38:43 +0200 Subject: [PATCH 79/96] Formatting fix. --- code_examples/theory/adl_niebloid_code.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code_examples/theory/adl_niebloid_code.h b/code_examples/theory/adl_niebloid_code.h index 0b90065..422be90 100644 --- a/code_examples/theory/adl_niebloid_code.h +++ b/code_examples/theory/adl_niebloid_code.h @@ -6,8 +6,8 @@ namespace impl { struct DoSomethingFn { template void operator()(T&& arg) const requires HasCustomImpl { - do_something(std::forward(arg)); - } + do_something(std::forward(arg)); + } template void operator()(T&&) const requires (!HasCustomImpl) { /* default implementation */ } From b62ac19023893aff5a6b0eb6ac104849f3ff2d94 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 19:50:59 +0200 Subject: [PATCH 80/96] Version 0.3 update --- book.tex | 8 ++--- chapters/02_introduction.tex | 2 ++ chapters/03_algorithms_02_swaps.tex | 30 ++----------------- chapters/03_algorithms_03_sorting.tex | 17 +++++++---- chapters/03_algorithms_04_partitioning.tex | 4 +-- chapters/03_algorithms_05_divide.tex | 2 +- chapters/03_algorithms_07_sets.tex | 4 +-- chapters/03_algorithms_08_transformations.tex | 6 ++-- .../03_algorithms_10_general_reductions.tex | 4 +-- chapters/03_algorithms_12_generators.tex | 8 ++--- chapters/03_algorithms_13_copies_moves.tex | 4 +-- chapters/03_algorithms_15_heap.tex | 6 ++-- chapters/03_algorithms_16_searching.tex | 2 +- chapters/03_algorithms_17_minmax.tex | 2 +- chapters/04_ranges_in_depth.tex | 8 ++--- chapters/90_theory.tex | 3 +- 16 files changed, 47 insertions(+), 63 deletions(-) diff --git a/book.tex b/book.tex index bff00c6..69bce2d 100644 --- a/book.tex +++ b/book.tex @@ -33,14 +33,14 @@ \frontmatter \input{chapters/00_title_page} -%\input{chapters/01_preface} % Reviewed +\input{chapters/01_preface} % Reviewed \mainmatter \tableofcontents -%\input{chapters/02_introduction} % In review -%\input{chapters/03_algorithms_00_main} % In review -%\input{chapters/04_ranges_in_depth} % In review +\input{chapters/02_introduction} % In review +\input{chapters/03_algorithms_00_main} % In review +\input{chapters/04_ranges_in_depth} % In review \input{chapters/90_theory} % TODO diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 70941f1..5420c20 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -144,6 +144,8 @@ \section{A simpler mental model for iterators} The benefit of thinking about the returned value as the end iterator of a range is that it removes the potential for corner cases. For example, what if the algorithm doesn't find any element out of order? The returned value will be the end iterator of the source range, meaning that the range returned is simply the entire source range. +\newpage + In some cases, a single returned iterator denotes multiple ranges. \begin{box-note} diff --git a/chapters/03_algorithms_02_swaps.tex b/chapters/03_algorithms_02_swaps.tex index ecfabde..a70fe58 100644 --- a/chapters/03_algorithms_02_swaps.tex +++ b/chapters/03_algorithms_02_swaps.tex @@ -1,32 +1,6 @@ \section{Swaps} -Before discussing swaps, we need to make a short detour and discuss argument-dependent lookup, an essential aspect of the pre-C++20 version of the std::swap algorithm. - -Argument-dependent lookup is relied upon heavily in C++, notably when overloading operators. - -\begin{box-note} -\footnotesize Example of argument-dependent lookup for \cpp{operator<<} implemented as a function. -\tcblower -\cppfile{code_examples/theory/adl_code.h} -\end{box-note} - -The situation changes slightly when the function is implemented as a friend function. Such a function is a member of the surrounding namespace. However, it is not visible outside of ADL. - -\begin{box-note} -\footnotesize Example of argument-dependent lookup for \cpp{operator<<} implemented as a friend function. -\tcblower -\cppfile{code_examples/theory/adl_friend_code.h} -\end{box-note} - -The benefit of relying on ADL is that there is no complexity on the caller's site. An unqualified call will invoke the correct overload. Except, this wouldn't be C++ if there weren't an exception to this rule. - -If on top of having the ability to specialize, we also want default behaviour as a fallback, the caller now needs to make sure to pull in the default overload to the local scope. - -\begin{box-note} -\footnotesize Example of argument-dependent lookup with a default templated version of the function. -\tcblower -\cppfile{code_examples/theory/adl_default_code.h} -\end{box-note} +Before C++11 and the introduction of move operations, swaps were the only way objects with value semantics could exchange content without involving a deep copy. \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} \index{\cpp{std::swap}} @@ -35,7 +9,7 @@ \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} \cppversions{\texttt{swap}}{\CC98}{\CC20}{N/A}{\CC20} -Correctly calling swap (as mentioned at the beginning of this section) requires pulling the default std::swap version to the local scope. +Correctly calling swap requires pulling the default std::swap version to the local scope. To read more on why this is needed check out the theory chapter of this book, specifically the section on ADL (\ref{theory:adl}). \begin{box-note} \footnotesize Example of correctly calling \cpp{std::swap}. diff --git a/chapters/03_algorithms_03_sorting.tex b/chapters/03_algorithms_03_sorting.tex index 66f5ae7..82356a3 100644 --- a/chapters/03_algorithms_03_sorting.tex +++ b/chapters/03_algorithms_03_sorting.tex @@ -35,7 +35,7 @@ \section{Sorting} \subsection{\texorpdfstring{\cpp{std::lexicographical_compare}}{\texttt{std::lexicographical\_compare}}} \index{\cpp{std::lexicographical_compare}} -Lexicographical \texttt{strict\_weak\_ordering} for ranges is exposed through the \texttt{std::lexicographical\_compare} algorithm. +Lexicographical \texttt{strict\_weak\_ordering} for ranges is exposed through the \newline\texttt{std::lexicographical\_compare} algorithm. \cppversions{\texttt{lex\dots compare}}{\CC98}{\CC20}{\CC17}{\CC20} @@ -58,7 +58,14 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare}}{\texttt{std::lex \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\texttt{std::lexicographical\_compare\_three\_way}}} \index{\cpp{std::lexicographical_compare_three_way}} -The \texttt{std::lexicographical\_compare\_three\_way} is the spaceship operator equivalent to \texttt{std::lexicographical\_compare}. It returns one of \texttt{std::strong\_ordering}, \texttt{std::weak\_ordering} or \texttt{std::partial\_ordering} types, depending on the type returned by the elements' spaceship operator. +The \texttt{std::lexicographical\_compare\_three\_way} is the spaceship operator equivalent to \texttt{std::lexicographical\_compare}. It returns one of: +\begin{itemize} + \item\texttt{std::strong\_ordering} + \item \texttt{std::weak\_ordering} + \item \texttt{std::partial\_ordering} +\end{itemize} + +The type depends on the type returned by the elements' spaceship operator. \cppversions{\texttt{lex\dots three\_way}}{\CC20}{\CC20}{N/A}{N/A} \constraints{\texttt{(input\_range, input\_range)}}{}{\texttt{operator<=>}}{\texttt{strong\_ordering}, \texttt{weak\_ordering}, \texttt{partial\_ordering}} @@ -140,7 +147,7 @@ \subsection{\texorpdfstring{\cpp{std::is_sorted_until}}{\texttt{std::is\_sorted\ \end{box-note} Note that because of the behaviour of \cpp{std::is_sorted_until}, the following is always true:\\ -\small\cpp{std::is_sorted(r.begin(), std::is_sorted_until(r.begin(), r.end()))} +\begin{small}\cpp{std::is_sorted(r.begin(), std::is_sorted_until(r.begin(), r.end()))}\end{small} \subsection{\texorpdfstring{\cpp{std::partial_sort}}{\texttt{std::partial\_sort}}} \index{\cpp{std::partial_sort}} @@ -188,8 +195,8 @@ \subsection{\texorpdfstring{\cpp{qsort}}{\texttt{qsort}} - C standard library} I would strongly recommend avoiding \cpp{qsort}, as \cpp{std::sort} and \cpp{std::ranges::sort} should be a better choice in every situation. Moreover, \cpp{qsort} is only valid for trivially copyable types, and those will correctly optimize to \cpp{memcpy}/\cpp{memmove} operations even when using \cpp{std::sort}. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using \cpp{std::sort} to achieve the same result as in the previous example. \tcblower \cppfile{code_examples/algorithms/qsort_not_code.h} -\end{box-note} \ No newline at end of file +\end{box-nobreak} \ No newline at end of file diff --git a/chapters/03_algorithms_04_partitioning.tex b/chapters/03_algorithms_04_partitioning.tex index c1d4e96..26460ce 100644 --- a/chapters/03_algorithms_04_partitioning.tex +++ b/chapters/03_algorithms_04_partitioning.tex @@ -45,11 +45,11 @@ \subsection{\texorpdfstring{\cpp{std::is_partitioned}}{\texttt{std::is\_partitio Note that a sorted range is always partitioned for any possible value (with a different partition point). -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using \cpp{std::is_partitioned}. \tcblower \cppfile{code_examples/algorithms/is_partitioned_code.h} -\end{box-note} +\end{box-nobreak} \subsection{\texorpdfstring{\cpp{std::partition_copy}}{\texttt{std::partition\_copy}}} \index{\cpp{std::partition_copy}} diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index 911a951..c865974 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -57,7 +57,7 @@ \subsection{\texorpdfstring{\cpp{std::equal_range}}{\texttt{std::equal\_range}}} \subsection{\texorpdfstring{\cpp{std::partition_point}}{\texttt{std::partition\_point}}} \index{\cpp{std::partition_point}} -Despite the naming, \cpp{std:partition_point} works very similarly to \cpp{std::upper_bound}, however instead of searching for a particular value, it searches using a predicate. +Despite the naming, \cpp{std:partition_point} works very similarly to \texttt{std::upper\-\_bound}, however instead of searching for a particular value, it searches using a predicate. \cppversions{\texttt{partition\_point}}{\CC11}{\CC20}{N/A}{\CC20} \constraints{\texttt{forward\_range}}{}{N/A}{\texttt{unary\_predicate}} diff --git a/chapters/03_algorithms_07_sets.tex b/chapters/03_algorithms_07_sets.tex index 2fbcbf9..e567d8e 100644 --- a/chapters/03_algorithms_07_sets.tex +++ b/chapters/03_algorithms_07_sets.tex @@ -34,11 +34,11 @@ \subsection{\texorpdfstring{\cpp{std::set_symmetric_difference}}{\texttt{std::se For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::abs(M-N)} such elements from the corresponding range. That is, if $M>N$, $M-N$ elements will be copied from the first range; otherwise, $N-M$ elements will be copied from the second range. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using \cpp{std::set_symmetric_difference}. \tcblower \cppfile{code_examples/algorithms/set_symmetric_difference_code.h} -\end{box-note} +\end{box-nobreak} \begin{box-note} \footnotesize Example demonstrating \cpp{std::set_symmetric_difference} behaviour when equivalent elements are present. diff --git a/chapters/03_algorithms_08_transformations.tex b/chapters/03_algorithms_08_transformations.tex index 1dbf64d..87b632a 100644 --- a/chapters/03_algorithms_08_transformations.tex +++ b/chapters/03_algorithms_08_transformations.tex @@ -93,7 +93,7 @@ \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} \cppfile{code_examples/algorithms/reverse_code.h} \end{box-note} -C-style arrays and C-style strings can be adapted using \cpp{std::span} and \cpp{std::string_view} to allow reverse iteration. +C-style arrays and C-style strings can be adapted using \cpp{std::span} and \texttt{std::string\-\_view} to allow reverse iteration. \begin{box-note} \footnotesize Example of using \cpp{std::span} and \cpp{std::string_view} to addapt C-style constructs for reverse iteration. @@ -104,7 +104,7 @@ \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} \index{\cpp{std::rotate}} -The \cpp{std::rotate} algorithm rearranges elements in the range from \cpp{[first, middle),[middle, last)} to \cpp{[middle, last),[first, middle)}. +The \cpp{std::rotate} algorithm rearranges elements in the range from \texttt{[first, middle),} \texttt{[middle, last)} to \texttt{[middle, last),} \texttt{[first, middle)}. \cppversions{\texttt{rotate}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{}{} @@ -118,7 +118,7 @@ \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} \subsection{\texorpdfstring{\cpp{std::shuffle}}{\texttt{std::shuffle}}} \index{\cpp{std::shuffle}} -The \cpp{std::shuffle} algorithm is a successor of the now-defunct \cpp{std::random_shuffle} algorithm (deprecated in \CC14, removed in \CC17) and relies on new random facilities added in \CC11. +The \cpp{std::shuffle} algorithm is a successor of the now-defunct (deprecated in \CC14, removed in \CC17) \cpp{std::random_shuffle} algorithm and relies on new random facilities added in \CC11. \cppversions{\texttt{shuffle}}{\CC11}{N/A}{N/A}{\CC20} \constraints{\texttt{random\_access\_range}}{}{}{} diff --git a/chapters/03_algorithms_10_general_reductions.tex b/chapters/03_algorithms_10_general_reductions.tex index 450a5a3..398abe6 100644 --- a/chapters/03_algorithms_10_general_reductions.tex +++ b/chapters/03_algorithms_10_general_reductions.tex @@ -14,7 +14,7 @@ \subsection{\texorpdfstring{\cpp{std::reduce}}{\texttt{std::reduce}}} \cppversions{\texttt{reduce}}{\CC17}{\CC20}{\CC17}{N/A} \constraints{\texttt{input\_range}}{\texttt{forward\_range}}{\texttt{std::plus<>()}}{\texttt{binary\_functor}} -Note that while we have access to a sequenced execution policy (\cpp{std::execution::seq}), this does not make \cpp{std::reduce} sequenced in a left-fold sense. +Note that while we have access to a sequenced execution policy (i.e. \newline\cpp{std::execution::seq}), this does not make \cpp{std::reduce} sequenced in a left-fold sense. \begin{box-note} \footnotesize Example of using \cpp{std::reduce} with and without an execution policy. @@ -35,7 +35,7 @@ \subsection{\texorpdfstring{\cpp{std::reduce}}{\texttt{std::reduce}}} \subsection{\texorpdfstring{\cpp{std::transform_reduce}}{\texttt{std::transform\_reduce}}} \index{\cpp{std::transform_reduce}} -The \cpp{std::transform_reduce} algorithm is the generalised counterpart to \cpp{std::inner_product}. On top of the two-range variant, the algorithm also provides a unary overload. +The \cpp{std::transform_reduce} algorithm is the generalised counterpart to \texttt{std::inner\-\_product}. On top of the two-range variant, the algorithm also provides a unary overload. \cppversions{\texttt{transform\_reduce}}{\CC17}{\CC20}{\CC17}{N/A} \constraints{\texttt{input\_range}}{\texttt{forward\_range}}{N/A}{\texttt{(binary\_functor, unary\_functor)}} diff --git a/chapters/03_algorithms_12_generators.tex b/chapters/03_algorithms_12_generators.tex index 9919ea6..89c2a15 100644 --- a/chapters/03_algorithms_12_generators.tex +++ b/chapters/03_algorithms_12_generators.tex @@ -50,12 +50,12 @@ \subsection{\texorpdfstring{\cpp{std::iota}}{\texttt{std::iota}}} \cppfile{code_examples/algorithms/iota_code.h} \end{box-note} -Notably, the \cpp{std::iota} algorithm is also an outlier in the support added with the C++20 standard. The std::iota algorithm did not receive a range version. However, we do have access to an iota view. +Notably, the \cpp{std::iota} algorithm is also an outlier in the support added with the C++20 standard. The \cpp{std::iota} algorithm did not receive a range version. However, we do have access to an iota view. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using both finite and infinite \cpp{std::views::iota}. \tcblower \cppfile{code_examples/algorithms/iota_view_code.h} -\end{box-note} +\end{box-nobreak} -Here we take advantage of the finite view constructor \cpp{std::views::iota(1,10)} to establish the output size (line 3), which allows us to use the infinite view \cpp{std::views::iota(5)} for the second parameter. Functionally, we could swap even the second view for a finite one. However, this would impose an additional (and unnecessary) boundary check. +Here we take advantage of the finite view constructor \cpp{std::views::iota(1,10)} to establish the output size (line 3), which allows us to use the infinite view \texttt{std::views\-::iota(5)} for the second parameter. Functionally, we could swap even the second view for a finite one. However, this would impose an additional (and unnecessary) boundary check. diff --git a/chapters/03_algorithms_13_copies_moves.tex b/chapters/03_algorithms_13_copies_moves.tex index a4c6ec1..2b2e5bd 100644 --- a/chapters/03_algorithms_13_copies_moves.tex +++ b/chapters/03_algorithms_13_copies_moves.tex @@ -50,11 +50,11 @@ \subsection{\texorpdfstring{\cpp{std::copy_backward}, \cpp{std::move_backward}}{ The output iterator cannot be within \cpp{(first, last]} and will be treated as the end iterator for the destination range, meaning that the algorithm will write the first value to \cpp{std::prev(end)}. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of a non-overlapping and permitted overlapping case of \cpp{std::copy_backward}. \tcblower \cppfile{code_examples/algorithms/copy_backward_code.h} -\end{box-note} +\end{box-nobreak} \subsection{\texorpdfstring{\cpp{std::copy_n}}{\texttt{std::copy\_n}}} \index{\cpp{std::copy_n}} diff --git a/chapters/03_algorithms_15_heap.tex b/chapters/03_algorithms_15_heap.tex index cf690a1..d7dd349 100644 --- a/chapters/03_algorithms_15_heap.tex +++ b/chapters/03_algorithms_15_heap.tex @@ -20,11 +20,11 @@ \subsection{\texorpdfstring{\cpp{std::make_heap}, \cpp{std::push_heap}, \cpp{std The \cpp{std::make_heap} algorithm reorders elements in the given range such that the elements maintain the max-heap property, that is, the element at index $i$ compares greater or equal to the elements at indexes $2i+1$ and $2i+2$. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using \cpp{std::make_heap} to construct a max-heap and a min-heap (using a custom comparator). \tcblower \cppfile{code_examples/algorithms/make_heap_code.h} -\end{box-note} +\end{box-nobreak} The \cpp{std::push_heap} and \cpp{std::pop_heap} algorithms simulate push and pop operations for the max-heap data structure. However, because they operate on top of a range, they cannot manipulate the underlying data structure. Therefore, they use the last element of the range as the input/output. @@ -61,7 +61,7 @@ \subsection{\texorpdfstring{\cpp{std::is_heap}, \cpp{std::is_heap_until}}{\textt \constraints{\texttt{random\_access\_range}}{}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -The two algorithms follow the same logic as \cpp{std::is_sorted} and \cpp{std::is_sorted_until}, returning a boolean and an iterator to the first out-of-order element respectively. +The two algorithms follow the same logic as \cpp{std::is_sorted} and \texttt{std::is\-\_sorted\-\_until}, returning a boolean and an iterator to the first out-of-order element respectively. \begin{box-note} \footnotesize Example of using \cpp{std::is_heap} and \cpp{std::is_heap_until}. diff --git a/chapters/03_algorithms_16_searching.tex b/chapters/03_algorithms_16_searching.tex index 4a385a7..c66e014 100644 --- a/chapters/03_algorithms_16_searching.tex +++ b/chapters/03_algorithms_16_searching.tex @@ -85,7 +85,7 @@ \subsection{\texorpdfstring{\cpp{std::search}, \cpp{std::find_end}}{\texttt{std: \index{\cpp{std::find_end}} Both \cpp{std::search} and \cpp{std::find_end} algorithms search for a sub-sequence in a sequence. -The \cpp{std::search} algorithm will return the first instance, and \cpp{std::find_end} will return the last. +The \cpp{std::search} algorithm will return the first instance, and \texttt{std::find\-\_end} will return the last. \cppversions{\texttt{search, find\_end}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_range)}}{\texttt{(forward\_range, forward\_range)}}{\texttt{operator==}}{\texttt{binary\_predicate}} diff --git a/chapters/03_algorithms_17_minmax.tex b/chapters/03_algorithms_17_minmax.tex index 176b044..3f5952b 100644 --- a/chapters/03_algorithms_17_minmax.tex +++ b/chapters/03_algorithms_17_minmax.tex @@ -105,4 +105,4 @@ \subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \new \cppfile{code_examples/algorithms/min_element_dangling_code.h} \end{box-note} -All ranged versions of algorithms that return iterators will return the \cpp{std::ranges::dangling} type when invoked on a temporary range. This would preclude the use case of using \cpp{std::span} to sub-reference a range, which is why the range algorithms have an additional concept of a \cpp{borrowed_range}. Such ranges can be passed in as temporaries since they do not own their elements. \ No newline at end of file +All ranged versions of algorithms that return iterators will return the \texttt{std::ranges\-::dangling} type when invoked on a temporary range. This would preclude the use case of using \cpp{std::span} to sub-reference a range, which is why the range algorithms have an additional concept of a \cpp{borrowed_range}. Such ranges can be passed in as temporaries since they do not own their elements. \ No newline at end of file diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index c99f13b..25ea711 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -14,11 +14,11 @@ \section{Reliance on concepts} First, the definitions of all concepts are now part of the standard library, e.g. you can look up what exactly it means for a type to be a \cpp{random_access_range} and also use these concepts to constrain your code. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using standard concepts in user code. The function accepts any random access range as the first argument and an output iterator with the same underlying type as the second argument. \tcblower \cppfile{code_examples/ranges/concepts_code.h} -\end{box-note} +\end{box-nobreak} Second, error messages now reference unsatisfied constraints instead of reporting an error deep in the library implementation. @@ -86,7 +86,7 @@ \section{Dangling iterator protection} \cppfile{code_examples/ranges/dangling_code.h} \end{box-note} -User types can declare themselves as borrowed ranges by specializing the \cpp{enable_borrowed_range} constant. +User types can declare themselves as borrowed ranges by specializing the \texttt{enable\-\_borrowed\-\_range} constant. \begin{box-note} \footnotesize Example demonstrating declaring MyView as a borrowed range. @@ -231,7 +231,7 @@ \subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split \index{\cpp{std::views::join_view}} The two split views split a single range into a view over sub-ranges. However, they differ in their implementation. -The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \cpp{std::view::lazy_split} does not, but it does support input ranges. +The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \texttt{std::view::\-lazy\_split} does not, but it does support input ranges. \begin{box-note} \footnotesize Example of using split view to parse a version number. diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 3c31311..76d2238 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -3,6 +3,7 @@ \chapter{Bits of C++ theory} This chapter will dive deep into the various topics referenced throughout the book. While this chapter serves as a reference, the topics are still presented in a heavily simplified, example-heavy format. For a proper reference, please refer to the C++ standard. \section{Argument-dependent lookup (ADL)} +\label{theory:adl} When calling a method without qualification (i.e. not specifying the namespace), the compiler needs to determine the set of candidate functions. As a first step, the compiler will do an unqualified name lookup, which starts at the local scope and examines the parent scopes until it finds the first instance of the name (at which point it stops). @@ -65,7 +66,7 @@ \subsection{\texorpdfstring{\CC20 ADL customization point}{C++20 ADL customizati With the introduction of concepts in \CC20, we now have a cleaner way to introduce a customization point using ADL. \begin{box-note} -\footnotesize Example. +\footnotesize The concept on line 4 will be satisfied if an ADL call is valid, i.e. there is a custom implementation. This is then used on lines 8 and 13 to differentiate between the two overloads. One calls the custom implementation, and the other contains the default implementation. The inline variable on line 18 is in an inline namespace to prevent collision with friend functions in the same namespace. However, because friend functions are only visible to ADL, a fully qualified call will always invoke this function object. \tcblower \cppfile{code_examples/theory/adl_niebloid_code.h} \end{box-note} From 28b77792141a0e842a412068610de51f36b2aa60 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 20:11:57 +0200 Subject: [PATCH 81/96] Update README.md Update for v0.3.0 release --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 37b767b..e4b1a8e 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v0.2.1)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.1/book_release_v0.2.1.pdf) +[Latest PDF release (v0.3.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.3.0/book_with_cover_v0.3.0.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.1/book_release_v0.2.1.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.3.0/book_with_cover_v0.3.0.pdf) ## Changelog +- `0.3.0` New chapter with ADL information and formatting cleanup. - `0.2.1` Fixed page numbering issue, small text changes. - `0.2.0` Added chapter covering C++20 ranges and views. - `0.1.1` Added index and a cover page, small text changes. From de0610961684b0d893909a1c259a3188f711805f Mon Sep 17 00:00:00 2001 From: Simone Gasparini Date: Tue, 27 Dec 2022 18:30:23 +0100 Subject: [PATCH 82/96] [ch03] missing formatting for std::unique --- chapters/03_algorithms_06_linear_sorted.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapters/03_algorithms_06_linear_sorted.tex b/chapters/03_algorithms_06_linear_sorted.tex index ff5767c..ffbd795 100644 --- a/chapters/03_algorithms_06_linear_sorted.tex +++ b/chapters/03_algorithms_06_linear_sorted.tex @@ -62,7 +62,7 @@ \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{s \index{\cpp{std::unique}} \index{\cpp{std::unique_copy}} -The std::unique algorithm removes consecutive duplicate values. The typical use case is in conjunction with a sorted range. However, this is not a requirement of std::unique. +The \cpp{std::unique} algorithm removes consecutive duplicate values. The typical use case is in conjunction with a sorted range. However, this is not a requirement of \cpp{std::unique}. \cppversions{\texttt{unique}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{\texttt{operator==}}{\texttt{binary\_predicate}} @@ -75,7 +75,7 @@ \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{s \cppfile{code_examples/algorithms/unique_code.h} \end{box-note} -The \cpp{std::unique_copy} is a variant of std::unique that outputs the unique values to a second range. +The \cpp{std::unique_copy} is a variant of \cpp{std::unique} that outputs the unique values to a second range. \cppversions{\texttt{unique\_copy}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{operator==}}{\texttt{binary\_predicate}} @@ -84,4 +84,4 @@ \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{s \footnotesize Example of using \cpp{std::unique_copy}. \tcblower \cppfile{code_examples/algorithms/unique_copy_code.h} -\end{box-note} \ No newline at end of file +\end{box-note} From 19574aa292c3bea3b7d3c0383881cc8633ad2b7c Mon Sep 17 00:00:00 2001 From: japm48 Date: Fri, 30 Dec 2022 23:05:16 +0100 Subject: [PATCH 83/96] disable ligatures in code --- packages/fonts.tex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/fonts.tex b/packages/fonts.tex index 66fea16..625447d 100644 --- a/packages/fonts.tex +++ b/packages/fonts.tex @@ -28,7 +28,8 @@ UprightFont=*-Regular, BoldFont=*-Bold, ItalicFont=*-Italic, - BoldItalicFont=*-BoldItalic + BoldItalicFont=*-BoldItalic, + Contextuals=AlternateOff ] \renewcommand{\familydefault}{\sfdefault} From 6b185726950dc25ee38d9984c3d549bb14fe8f1b Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 1 Feb 2023 14:53:25 +0000 Subject: [PATCH 84/96] Version 1.0.0 update. --- LICENSE.md | 2 +- book.tex | 48 +-- chapters/00_title_page.tex | 2 +- chapters/01_preface.tex | 12 +- chapters/02_introduction.tex | 44 +-- chapters/03_algorithms_01_foreach.tex | 24 +- chapters/03_algorithms_02_swaps.tex | 21 +- chapters/03_algorithms_03_sorting.tex | 56 ++-- chapters/03_algorithms_04_partitioning.tex | 20 +- chapters/03_algorithms_05_divide.tex | 28 +- chapters/03_algorithms_06_linear_sorted.tex | 24 +- chapters/03_algorithms_07_sets.tex | 60 ++-- chapters/03_algorithms_08_transformations.tex | 87 +++--- chapters/03_algorithms_09_left_folds.tex | 67 ++++- .../03_algorithms_10_general_reductions.tex | 26 +- chapters/03_algorithms_11_boolean.tex | 5 +- chapters/03_algorithms_12_generators.tex | 18 +- chapters/03_algorithms_13_copies_moves.tex | 40 +-- chapters/03_algorithms_14_uninitialized.tex | 21 +- chapters/03_algorithms_15_heap.tex | 24 +- chapters/03_algorithms_16_searching.tex | 48 ++- chapters/03_algorithms_17_minmax.tex | 32 +- chapters/04_ranges_in_depth.tex | 284 ------------------ chapters/04_views_00_main.tex | 2 + chapters/04_views_01_intro_ranges.tex | 116 +++++++ chapters/04_views_02_the_views.tex | 178 +++++++++++ chapters/90_theory.tex | 186 +++++++++++- chapters/XX_index.tex | 1 - chapters/Y4_TODO.tex | 16 - chapters/Y4_making_your_own.tex | 1 - chapters/Y5_parallel_algorithms.tex | 1 - chapters/Y8_extra_topics.tex | 191 +----------- code_examples/algorithms/accumulate_code.h | 6 +- .../algorithms/accumulate_right_code.h | 3 +- .../algorithms/adjacent_difference_code.h | 2 +- .../adjacent_difference_extra_code.h | 2 +- .../algorithms/adjacent_difference_par_code.h | 6 + code_examples/algorithms/adjacent_find_code.h | 2 +- code_examples/algorithms/clamp_code.h | 2 +- code_examples/algorithms/copy_backward_code.h | 2 +- code_examples/algorithms/copy_code.h | 2 +- code_examples/algorithms/copy_if_code.h | 2 +- code_examples/algorithms/copy_n_code.h | 2 +- .../algorithms/equal_with_range_code.h | 4 +- .../algorithms/exclusive_scan_code.h | 2 +- code_examples/algorithms/fill_n_code.h | 2 +- code_examples/algorithms/find_code.h | 3 +- code_examples/algorithms/find_first_of_code.h | 2 +- code_examples/algorithms/find_if_code.h | 2 +- .../algorithms/for_each_code_parallel.h | 3 +- .../algorithms/for_each_code_range.h | 3 +- .../algorithms/for_each_code_simple.h | 3 +- code_examples/algorithms/for_each_n_code.h | 2 +- code_examples/algorithms/generate_code.h | 2 +- code_examples/algorithms/includes_code.h | 3 +- .../algorithms/inclusive_scan_code.h | 2 +- code_examples/algorithms/inner_product_code.h | 2 +- .../algorithms/inner_product_one_code.h | 2 +- code_examples/algorithms/iota_code.h | 2 +- .../algorithms/is_partitioned_code.h | 2 +- .../algorithms/is_sorted_until_code.h | 2 +- .../algorithms/iter_swap_partition_code.h | 2 +- .../algorithms/iter_swap_unique_ptr_code.h | 2 +- .../algorithms/lexicographical_compare_code.h | 2 +- .../lexicographical_compare_three_way_code.h | 6 +- code_examples/algorithms/lower_bound_code.h | 6 +- code_examples/algorithms/make_heap_code.h | 2 +- code_examples/algorithms/merge_code.h | 3 +- code_examples/algorithms/min_max_code.h | 3 +- code_examples/algorithms/minmax_code.h | 2 +- code_examples/algorithms/minmax_extra_code.h | 2 +- code_examples/algorithms/mismatch_code.h | 5 +- code_examples/algorithms/move_code.h | 3 +- code_examples/algorithms/nth_element_code.h | 11 +- code_examples/algorithms/partial_sort_code.h | 2 +- .../algorithms/partial_sort_copy_code.h | 3 +- code_examples/algorithms/partial_sum_code.h | 2 +- code_examples/algorithms/partition_code.h | 2 +- .../algorithms/partition_copy_code.h | 2 +- code_examples/algorithms/push_heap_code.h | 2 +- code_examples/algorithms/qsort_code.h | 2 +- code_examples/algorithms/qsort_not_code.h | 2 +- code_examples/algorithms/reduce_code.h | 5 +- code_examples/algorithms/reduce_noinit_code.h | 2 +- code_examples/algorithms/replace_copy_code.h | 5 +- code_examples/algorithms/reverse_copy_code.h | 2 +- code_examples/algorithms/rotate_copy_code.h | 5 +- code_examples/algorithms/search_code.h | 2 +- code_examples/algorithms/search_n_code.h | 2 +- code_examples/algorithms/searchers_code.h | 4 +- .../algorithms/set_difference_equal_code.h | 3 +- .../algorithms/set_intersection_equal_code.h | 3 +- .../set_symmetric_difference_equal_code.h | 10 +- code_examples/algorithms/set_union_code.h | 2 +- .../algorithms/set_union_equal_code.h | 9 +- code_examples/algorithms/shift_code.h | 7 + code_examples/algorithms/shift_move_code.h | 20 ++ code_examples/algorithms/shuffle_code.h | 3 +- code_examples/algorithms/sort_code.h | 2 +- code_examples/algorithms/sort_heap_code.h | 2 +- .../algorithms/sort_projection_code.h | 2 +- .../algorithms/stable_partition_code.h | 2 +- code_examples/algorithms/stable_sort_code.h | 7 +- code_examples/algorithms/swap_calling_code.h | 2 +- code_examples/algorithms/swap_ranges_code.h | 2 +- .../algorithms/transform_inclusive_code.h | 2 +- .../algorithms/transform_reduce_code.h | 2 +- .../algorithms/uninitialized_constr_code.h | 17 +- .../algorithms/uninitialized_copy_code.h | 3 +- code_examples/ranges/borrowed_optin_code.h | 4 +- code_examples/ranges/projected_type_code.h | 4 +- code_examples/ranges/stateful_code.h | 22 ++ code_examples/theory/deduction_algorithm.h | 8 + code_examples/theory/deduction_concepts.h | 5 + code_examples/theory/floating_point.h | 9 + .../theory/floating_point_ordering.h | 19 ++ .../theory/integral_conversions_different_a.h | 5 + .../theory/integral_conversions_different_b.h | 5 + .../theory/integral_conversions_different_c.h | 5 + .../theory/integral_conversions_problems.h | 9 + .../theory/integral_conversions_same.h | 5 + code_examples/theory/integral_promotions.h | 5 + code_examples/theory/references.h | 15 + code_examples/theory/safe_compare.h | 11 + code_examples/theory/safe_in_range.h | 6 + code_examples/theory/safe_ssize.h | 8 + code_examples/views/elements_code.h | 8 +- code_examples/views/keys_values_code.h | 2 +- packages/color_blocks.tex | 28 -- packages/custom_commands/CC.tex | 4 + packages/custom_commands/ExternalLink.tex | 22 ++ packages/custom_commands/circled.tex | 3 + packages/custom_commands/codebox.tex | 26 ++ .../constraints.tex} | 22 -- .../cpp.tex} | 14 +- packages/custom_commands/cppversions.tex | 18 ++ packages/custom_commands/version.tex | 1 + packages/fonts.tex | 6 +- packages/glossary_and_index.tex | 4 - packages/hyperref.tex | 9 + packages/packages.tex | 20 ++ 141 files changed, 1230 insertions(+), 1054 deletions(-) delete mode 100644 chapters/04_ranges_in_depth.tex create mode 100644 chapters/04_views_00_main.tex create mode 100644 chapters/04_views_01_intro_ranges.tex create mode 100644 chapters/04_views_02_the_views.tex delete mode 100644 chapters/XX_index.tex delete mode 100644 chapters/Y4_TODO.tex delete mode 100644 chapters/Y4_making_your_own.tex delete mode 100644 chapters/Y5_parallel_algorithms.tex create mode 100644 code_examples/algorithms/adjacent_difference_par_code.h create mode 100644 code_examples/algorithms/shift_code.h create mode 100644 code_examples/algorithms/shift_move_code.h create mode 100644 code_examples/ranges/stateful_code.h create mode 100644 code_examples/theory/deduction_algorithm.h create mode 100644 code_examples/theory/deduction_concepts.h create mode 100644 code_examples/theory/floating_point.h create mode 100644 code_examples/theory/floating_point_ordering.h create mode 100644 code_examples/theory/integral_conversions_different_a.h create mode 100644 code_examples/theory/integral_conversions_different_b.h create mode 100644 code_examples/theory/integral_conversions_different_c.h create mode 100644 code_examples/theory/integral_conversions_problems.h create mode 100644 code_examples/theory/integral_conversions_same.h create mode 100644 code_examples/theory/integral_promotions.h create mode 100644 code_examples/theory/references.h create mode 100644 code_examples/theory/safe_compare.h create mode 100644 code_examples/theory/safe_in_range.h create mode 100644 code_examples/theory/safe_ssize.h delete mode 100644 packages/color_blocks.tex create mode 100644 packages/custom_commands/CC.tex create mode 100644 packages/custom_commands/ExternalLink.tex create mode 100644 packages/custom_commands/circled.tex create mode 100644 packages/custom_commands/codebox.tex rename packages/{local.tex => custom_commands/constraints.tex} (55%) rename packages/{code_blocks.tex => custom_commands/cpp.tex} (76%) create mode 100644 packages/custom_commands/cppversions.tex create mode 100644 packages/custom_commands/version.tex delete mode 100644 packages/glossary_and_index.tex create mode 100644 packages/hyperref.tex create mode 100644 packages/packages.tex diff --git a/LICENSE.md b/LICENSE.md index 5dbf94c..6cce485 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -(c) RNDr. Šimon Tóth 2022 (business@simontoth.eu) +(c) RNDr. Šimon Tóth 2022-2023 (business@simontoth.eu) # Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License diff --git a/book.tex b/book.tex index 69bce2d..08e9dee 100644 --- a/book.tex +++ b/book.tex @@ -1,53 +1,21 @@ -%\documentclass[10pt,b5paper,svgnames,table,twoside]{memoir} -%\documentclass[11pt,a4paper,svgnames,table,twoside]{memoir} \documentclass[11pt,svgnames,table,a4paper]{book} -%\usepackage{showframe} -%\checkandfixthelayout - -\newcommand{\version}{0.3.0} -\usepackage{hyperref} -\hypersetup{ - colorlinks=true, - linkcolor=blue, - filecolor=magenta, - urlcolor=cyan, - pdftitle={Standard C++ Algorithms}, - pdfpagemode=FullScreen, - } -\usepackage{enumitem} -\newcommand*\circled[1]{\tikz[baseline=(char.base)]{ - \node[shape=circle,draw,inner sep=1pt] (char) {#1};}} - -\input{packages/fonts} -\input{packages/color_blocks} -\input{packages/code_blocks} -\input{packages/glossary_and_index} -\input{packages/local} - -\title{The 114 C++ standard algorithms\\and other related topics} - -\author{RNDr. Šimon Tóth} -\date{2022} +\input{packages/packages} \begin{document} \frontmatter \input{chapters/00_title_page} - -\input{chapters/01_preface} % Reviewed +\input{chapters/01_preface} \mainmatter \tableofcontents - -\input{chapters/02_introduction} % In review -\input{chapters/03_algorithms_00_main} % In review -\input{chapters/04_ranges_in_depth} % In review - -\input{chapters/90_theory} % TODO + +\input{chapters/02_introduction} +\input{chapters/03_algorithms_00_main} +\input{chapters/04_views_00_main} +\input{chapters/90_theory} \appendix -\input{chapters/XX_index} - -% Section on iterator invalidation +\printindex \backmatter diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index ce7d96f..402505f 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -23,7 +23,7 @@ \parindent 0pt \parskip \baselineskip \vfill -\textcopyright{} 2022 Šimon Tóth \\ +\textcopyright{} 2022-2023 Šimon Tóth \\ All rights reserved. This work may be distributed and/or modified under the conditions of the CC-BY-NC-SA license. diff --git a/chapters/01_preface.tex b/chapters/01_preface.tex index b273196..52d5d3f 100644 --- a/chapters/01_preface.tex +++ b/chapters/01_preface.tex @@ -30,10 +30,16 @@ \section*{Why cc-by-sa-nc} Explicitly, any personal use is permitted. For example, you can read, print, or share the book with your friends. -If you want to use this content where you are unsure whether you fit within the Creative Commons commercial definition\footnote{primarily intended for or directed toward commercial advantage or monetary compensation}, feel free to contact me on \href{https://twitter.com/SimonToth83}{Twitter}, \href{https://cz.linkedin.com/in/simontoth}{LinkedIn} or by \href{mailto:business@simontoth.eu}{email} (my DMs are always open). +If you want to use this content where you are unsure whether you fit within the Creative Commons commercial definition\footnote{primarily intended for or directed toward commercial advantage or monetary compensation}, feel free to contact me on \href{https://hachyderm.io/@simontoth}{Mastodon}, \href{https://cz.linkedin.com/in/simontoth}{LinkedIn} or by \href{mailto:business@simontoth.eu}{email} (my DMs are always open). \section*{Book status} -The book is currently in pre-release. +This book is currently content-complete (upto and including \CC20). -To keep up with the changes, visit the hosting repository: \url{https://github.com/HappyCerberus/book-cpp-algorithms}. Internal changelog will be kept after version 1.0 is released. \ No newline at end of file +To keep up with the changes, visit the hosting repository: \url{https://github.com/HappyCerberus/book-cpp-algorithms}. + +\subsection*{Changelog} + +\begin{enumerate} + \item[1.0] First complete release +\end{enumerate} \ No newline at end of file diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 5420c20..50599c2 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -11,33 +11,33 @@ \section{History of standard \texorpdfstring{\CC}{C++} algorithms} The \CC98 standard introduced most of the algorithms. However, it was the \CC11 standard with its introduction of lambdas that made algorithms worthwhile. Before lambdas, the time investment of writing a custom function object made the usefulness of algorithms dubious. \raggedbottom -\begin{box-nobreak} +\begin{codebox}[]{\href{https://godbolt.org/z/73r9n1WYM}{\ExternalLink}} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm with a custom function object, calculating the number of elements and their sum. \tcblower \cppfile{code_examples/introduction/history_cc98_code.h} -\end{box-nobreak} +\end{codebox} -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/zv4fWbT3z}{\ExternalLink}} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm with a capturing lambda, calculating the number of elements and their sum. \tcblower \cppfile{code_examples/introduction/history_cc11_code.h} -\end{box-note} +\end{codebox} The \CC17 standard introduced parallel algorithms that provide an easy way to speed up processing with minimal effort. All you need to do is to specify the desired execution model, and the library will take care of parallelizing the execution. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/1nK5944K7}{\ExternalLink}} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm using unsequenced parallel execution model. Note that counters are now shared state and need to be \cpp{std::atomic}\index{\cpp{std::atomic}} or protected by a \cpp{std::mutex}\index{\cpp{std::mutex}}. \tcblower \cppfile{code_examples/introduction/history_cc17_code.h} -\end{box-note} +\end{codebox} Finally, the \CC20 standard introduced a significant re-design in the form of ranges and views. Range versions of algorithms can now operate on ranges instead of \cpp{begin} and \cpp{end} iterators and views provide lazily evaluated versions of algorithms and utilities. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/57TGnzzxc}{\ExternalLink}} \footnotesize Example of the range version of the \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm. \tcblower \cppfile{code_examples/introduction/history_cc20_code.h} -\end{box-note} +\end{codebox} As of the time of writing, the \CC23 standard is not finalized. However, we already know that it will introduce more range algorithms, more views and the ability to implement custom views. @@ -51,19 +51,19 @@ \section{Iterators and ranges} To reference the entire content of a data structure, we can use the \cpp{begin()} and \cpp{end()} methods that return an iterator to the first element and an iterator one past the last element, respectively. Hence, the range \cpp{[begin, end)} contains all data structure elements. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/Wj4YjE51v}{\ExternalLink}} \footnotesize Example of specifying a range using two iterators. \tcblower \cppfile{code_examples/introduction/iterators_code.h} -\end{box-note} +\end{codebox} Sentinels follow the same idea. However, they do not need to be of an iterator type. Instead, they only need to be comparable to an iterator. The exclusive end of the range is then the first iterator that compares equal to the sentinel. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/qKrMo7scn}{\ExternalLink}} \footnotesize Example of specifying a range using an iterator and custom sentinel. The sentinel will compare \cpp{true} with iterators at least the given distance from the start iterator, therefore defining a range with the specified number of elements. \tcblower \cppfile{code_examples/introduction/sentinels_code.h} -\end{box-note} +\end{codebox} \subsection{Iterator categories} @@ -82,11 +82,11 @@ \subsection{Iterator categories} \textit{arrays, e.g. std::vector}\index{\cpp{std::vector}} \end{description} -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/dcaMEdnoM}{\ExternalLink}} \footnotesize Example demonstrating the difference between a random access iterator provided by \cpp{std::vector} and a bidirectional iterator provided by \cpp{std::list}. \tcblower \cppfile{code_examples/introduction/categories_code.h} -\end{box-note} +\end{codebox} \subsection{Range categories} @@ -128,19 +128,19 @@ \section{A simpler mental model for iterators} Ranges passed in as arguments are usually apparent, typically specified by pair of iterators. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/zTh9rn5E4}{\ExternalLink}} \footnotesize Example with two ranges passed in as an argument. The input range is fully specified, and the end iterator for the output range is implied from the number of elements in the input range. \tcblower \cppfile{code_examples/introduction/mental_range_code.h} -\end{box-note} +\end{codebox} The returned range can also be evident from the semantics of the algorithm. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/escqfWsnE}{\ExternalLink}} \footnotesize Example of \cpp{std::is_sorted_until} that returns an iterator to the first out-of-order element, which can also be thought as the end iterator for a maximal sorted sub-range. \tcblower \cppfile{code_examples/introduction/mental_sorted_code.h} -\end{box-note} +\end{codebox} The benefit of thinking about the returned value as the end iterator of a range is that it removes the potential for corner cases. For example, what if the algorithm doesn't find any element out of order? The returned value will be the end iterator of the source range, meaning that the range returned is simply the entire source range. @@ -148,16 +148,16 @@ \section{A simpler mental model for iterators} In some cases, a single returned iterator denotes multiple ranges. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/YEGKPToz7}{\ExternalLink}} \footnotesize Example of \cpp{std::lower_bound} that splits the range into two sub-ranges. \tcblower \cppfile{code_examples/introduction/mental_two_code.h} -\end{box-note} +\end{codebox} Even when the algorithm returns an iterator to a specific element, it might be worth considering the implied range. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/Er15W6K5W}{\ExternalLink}} \footnotesize Example of \cpp{std::find} establishing a prefix range that doesn't contain the searched-for element. \tcblower \cppfile{code_examples/introduction/mental_find_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_01_foreach.tex b/chapters/03_algorithms_01_foreach.tex index 2acd028..0c7e8b3 100644 --- a/chapters/03_algorithms_01_foreach.tex +++ b/chapters/03_algorithms_01_foreach.tex @@ -1,6 +1,6 @@ \section{Introducing the algorithms} -In this chapter, we introduce each of the 114 standard algorithms. The groups of algorithms are arbitrary and mainly introduced for presentation clarity. Therefore, you might correctly argue that a specific algorithm would be better suited to reside in a different group. +In this chapter, we introduce each of the standard algorithms. The groups of algorithms are arbitrary and mainly introduced for presentation clarity. Therefore, you might correctly argue that a specific algorithm would be better suited to reside in a different group. Before we start, we will use the \cpp{std::for_each} and \cpp{std::for_each_n} algorithms to demonstrate this chapter's structure for each algorithm. @@ -38,35 +38,35 @@ \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} \circled{4} The C++11 standard introduced the range-based for loop, which mostly replaced the uses of \cpp{std::for_each}. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/b6qEoonno}{\ExternalLink}} \footnotesize Example of a range loop over all elements of a \cpp{std::vector}. \tcblower \cppfile{code_examples/algorithms/for_each_code_range.h} -\end{box-note} +\end{codebox} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/M38M379To}{\ExternalLink}} \footnotesize Example of a \cpp{std::for_each} loop over all elements of a \cpp{std::vector}. \tcblower \cppfile{code_examples/algorithms/for_each_code_simple.h} -\end{box-note} +\end{codebox} However, there are still a few corner cases when using \cpp{std::for_each} is preferable. The first case is straightforward parallelization. Invoking an expensive operation for each element in parallel is trivial with \cpp{std::for_each}. As long as the operations are independent, there is no need for synchronization primitives. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/3bq4xT3G9}{\ExternalLink}} \footnotesize Example of a parallel \cpp{std::for_each} invoking a method on each element independently in parallel. \tcblower \cppfile{code_examples/algorithms/for_each_code_parallel.h} -\end{box-note} +\end{codebox} Second, the range version can provide a more concise and explicit syntax in some cases because of the projection support introduced in C++20. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/sfxWa4T5f}{\ExternalLink}} \footnotesize Example of the range version of \cpp{std::ranges::for_each} utilizing a projection to invoke the method \cpp{getValue()} (line 13) on each element and summing the resulting values using a lambda (line 12). \tcblower \cppfile{code_examples/algorithms/for_each_code_cpp20.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::for_each_n}}{\texttt{std::for\_each\_n}}} \index{\cpp{std::for_each_n}} @@ -95,10 +95,12 @@ \subsection{\texorpdfstring{\cpp{std::for_each_n}}{\texttt{std::for\_each\_n}}} \circled{4} While \cpp{std::for_each} operates on the entire range, the interval $[begin, end)$, \cpp{std::for_each_n} operates on the range $[first, first+n)$. Importantly, because the algorithm does not have access to the end iterator of the source range, it does no out-of-bounds checking, and it is the responsibility of the caller to ensure that the range $[first, first+n)$ is valid. -\begin{box-note} +\raggedbottom + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1hfje76Yq}{\ExternalLink}} \footnotesize Example demonstrating multiple uses of \cpp{std::for_each_n}. \tcblower \cppfile{code_examples/algorithms/for_each_n_code.h} -\end{box-note} +\end{codebox} Sending invitations to the \texttt{MAIN\_SEATS} top players is done in parallel (lines 4-7). Then all users' scores are stored in chunks of \texttt{PAGE\_SIZE} records (lines 13-15). Note that calculating the remaining number of elements (line 12) and jumping ahead by the number of stored elements (line 17) requires a random access iterator (in this case, provided by \cpp{std::vector}). \ No newline at end of file diff --git a/chapters/03_algorithms_02_swaps.tex b/chapters/03_algorithms_02_swaps.tex index a70fe58..bdf207d 100644 --- a/chapters/03_algorithms_02_swaps.tex +++ b/chapters/03_algorithms_02_swaps.tex @@ -11,12 +11,11 @@ \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} Correctly calling swap requires pulling the default std::swap version to the local scope. To read more on why this is needed check out the theory chapter of this book, specifically the section on ADL (\ref{theory:adl}). -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/Yzvzrb4rY}{\ExternalLink}} \footnotesize Example of correctly calling \cpp{std::swap}. \tcblower \cppfile{code_examples/algorithms/swap_calling_code.h} -\end{box-note} -\newpage +\end{codebox} The C++20 rangified version of swap removes this complexity, and it will: @@ -26,11 +25,11 @@ \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} \item if the parameters are also not arrays, it will default to a move-swap \end{itemize} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/8GxdMxsan}{\ExternalLink}} \footnotesize Example of specializing and calling \cpp{std::ranges::swap}. \tcblower \cppfile{code_examples/algorithms/swap_range_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::iter_swap}}{\texttt{std::iter\_swap}}} \index{\cpp{std::iter_swap}} @@ -41,19 +40,19 @@ \subsection{\texorpdfstring{\cpp{std::iter_swap}}{\texttt{std::iter\_swap}}} \constraints{(\cpp{forward_iterator}, \cpp{forward_iterator}) (non-range)}{}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/77enEEh4c}{\ExternalLink}} \footnotesize Example demonstrating the use \cpp{std::iter_swap} in a generic two-way partition algorithm. The algorithm uses concepts to constrain the acceptable types of its arguments. \tcblower \cppfile{code_examples/algorithms/iter_swap_partition_code.h} -\end{box-note} +\end{codebox} The range version extends the functionality to other dereferenceable objects. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/bxeP3PPaE}{\ExternalLink}} \footnotesize Example demonstrating the use of range version of \cpp{std::ranges::iter_swap} to swap the values pointed to by two instances of \cpp{std::unique_ptr}. \tcblower \cppfile{code_examples/algorithms/iter_swap_unique_ptr_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::swap_ranges}}{\texttt{std::swap\_ranges}}} \index{\cpp{std::swap_ranges}} @@ -62,8 +61,8 @@ \subsection{\texorpdfstring{\cpp{std::swap_ranges}}{\texttt{std::swap\_ranges}}} \cppversions{\texttt{swap\_ranges}}{\CC98}{\CC20}{\CC17}{\CC20} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/aEPe66f1E}{\ExternalLink}} \footnotesize Example of swapping the first three elements of an array with the last three elements using \cpp{std::swap_ranges}. Note the reversed order of elements due to the use of \cpp{rbegin}. \tcblower \cppfile{code_examples/algorithms/swap_ranges_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_03_sorting.tex b/chapters/03_algorithms_03_sorting.tex index 82356a3..4351d54 100644 --- a/chapters/03_algorithms_03_sorting.tex +++ b/chapters/03_algorithms_03_sorting.tex @@ -14,11 +14,11 @@ \section{Sorting} Since C++20 introduced the spaceship operator, user-defined types can easily access the default version of lexicographical ordering. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/7PjY8fc1G}{\ExternalLink}} \footnotesize Example of three approaches to implementing lexicographical comparison for a custom type. \tcblower \cppfile{code_examples/theory/comparable_code.h} -\end{box-note} +\end{codebox} The default lexicographical ordering (line 14) works recursively. It starts with the object’s bases first, left-to-right, depth-first and then non-static members in declaration order (processing arrays element by element, left-to-right). @@ -41,19 +41,19 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare}}{\texttt{std::lex \constraints{(\cpp{input_range}, \cpp{input_range})}{(\cpp{forward_range}, \cpp{forward_range})}{\cpp{operator<}}{\cpp{strict_weak_ordering}} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/YGW4ET1oa}{\ExternalLink}} \footnotesize Example of using \cpp{lexicographical_compare} and the built-in less than operator to compare vectors of integers. \tcblower \cppfile{code_examples/algorithms/lexicographical_compare_code.h} -\end{box-note} +\end{codebox} Because the standard containers already offer a built-in lexicographical comparison, the algorithm mainly finds use for comparing raw C arrays and in cases when we need to specify a custom comparator. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/djs6TGsEs}{\ExternalLink}} \footnotesize Example of using \cpp{lexicographical_compare} for C-style arrays and customizing the comparator. \tcblower \cppfile{code_examples/algorithms/lexicographical_compare_useful_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\texttt{std::lexicographical\_compare\_three\_way}}} \index{\cpp{std::lexicographical_compare_three_way}} @@ -70,11 +70,11 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\textt \cppversions{\texttt{lex\dots three\_way}}{\CC20}{\CC20}{N/A}{N/A} \constraints{\texttt{(input\_range, input\_range)}}{}{\texttt{operator<=>}}{\texttt{strong\_ordering}, \texttt{weak\_ordering}, \texttt{partial\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vrEqPaEEz}{\ExternalLink}} \footnotesize Example of using \cpp{std::lexicographical_compare_three_way}. \tcblower \cppfile{code_examples/algorithms/lexicographical_compare_three_way_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::sort}}{\texttt{std::sort}}} \index{\cpp{std::sort}} @@ -86,19 +86,19 @@ \subsection{\texorpdfstring{\cpp{std::sort}}{\texttt{std::sort}}} Due to the $O(n*logn)$ complexity guarantee, \cpp{std::sort} only operates on \cpp{random_access} ranges. Notably, \cpp{std::list} offers a method with an approximate $n*logn$ complexity. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vef61TWj9}{\ExternalLink}} \footnotesize Basic example of using \cpp{std::sort} and \cpp{std::list::sort}. \tcblower \cppfile{code_examples/algorithms/sort_code.h} -\end{box-note} +\end{codebox} With C++20, we can take advantage of projections to sort by a method or member: -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/4aGenq9b6}{\ExternalLink}} \footnotesize Example of using a projection in conjunction with a range algorithm. The algorithm will sort the elements based on the values obtained by invoking the method \cpp{value} on each element. \tcblower \cppfile{code_examples/algorithms/sort_projection_code.h} -\end{box-note} +\end{codebox} Before C++14, you would have to fully specify the type of the comparator, i.e. \cpp{std::greater{}}. The type erased variant \cpp{std::greater<>{}} relies on type deduction to determine the parameter types. Projections accept an unary invocable, including pointers to members and member functions. @@ -112,11 +112,11 @@ \subsection{\texorpdfstring{\cpp{std::stable_sort}}{\texttt{std::stable\_sort}}} If additional memory is available, stable\_sort remains $O(n*logn)$. However, if it fails to allocate, it will degrade to an $O(n*logn*logn)$ algorithm. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TKx8qP8bK}{\ExternalLink}} \footnotesize Example of re-sorting a range using \cpp{std::stable_sort}, resulting in a guaranteed order of elements. \tcblower \cppfile{code_examples/algorithms/stable_sort_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_sorted}}{\texttt{std::is\_sorted}}} \index{\cpp{std::is_sorted}} @@ -126,11 +126,11 @@ \subsection{\texorpdfstring{\cpp{std::is_sorted}}{\texttt{std::is\_sorted}}} \cppversions{\texttt{is\_sorted}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{\cpp{std::less}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/T3n9bfqdM}{\ExternalLink}} \footnotesize Example of testing a range using \cpp{std::is_sorted}. \tcblower \cppfile{code_examples/algorithms/is_sorted_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_sorted_until}}{\texttt{std::is\_sorted\_until}}} \index{\cpp{std::is_sorted_until}} @@ -140,11 +140,11 @@ \subsection{\texorpdfstring{\cpp{std::is_sorted_until}}{\texttt{std::is\_sorted\ \cppversions{\texttt{is\_sorted\_until}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{\texttt{std::less}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1dvboE6b1}{\ExternalLink}} \footnotesize Example of testing a range using \cpp{std::is_sorted_until}. \tcblower \cppfile{code_examples/algorithms/is_sorted_until_code.h} -\end{box-note} +\end{codebox} Note that because of the behaviour of \cpp{std::is_sorted_until}, the following is always true:\\ \begin{small}\cpp{std::is_sorted(r.begin(), std::is_sorted_until(r.begin(), r.end()))}\end{small} @@ -160,43 +160,43 @@ \subsection{\texorpdfstring{\cpp{std::partial_sort}}{\texttt{std::partial\_sort} The benefit of using a partial sort is faster runtime — approximately $O(n*logk)$, where k is the number of elements sorted. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/j6xjM4GnT}{\ExternalLink}} \footnotesize Example of using \cpp{std::partial_sort} to sort the first three elements of a range. \tcblower \cppfile{code_examples/algorithms/partial_sort_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::partial_sort_copy}}{\texttt{std::partial\_sort\_copy}}} \index{\cpp{std::partial_sort_copy}} -The \cpp{std::partial_sort_copy} algorithm has the same behaviour as \cpp{std::partial_sort}; however, it does not operate inline. Instead, the algorithm writes the results to a second range. +The \cpp{std::partial_sort_copy} algorithm has the same behaviour as \linebreak\cpp{std::partial_sort}; however, it does not operate inline. Instead, the algorithm writes the results to a second range. \cppversions{\texttt{partial\_sort\_copy}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\cpp{input_range -> random_access_range}}{\cpp{forward_range -> random_access_range}}{\cpp{operator<}}{\texttt{strict\_weak\_ordering}} The consequence of writing output to a second range is that the source range does not have to be mutable nor provide random access. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vjKc5nY31}{\ExternalLink}} \footnotesize Example of using \cpp{std::partial_sort_copy} to iterate over ten integers read from standard input and storing the top three values in sorted order. \tcblower \cppfile{code_examples/algorithms/partial_sort_copy_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{qsort}}{\texttt{qsort}} - C standard library} \index{\cpp{qsort}} Because the C standard library is part of the C++ standard library, we also have access to \cpp{qsort}. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Y4E63aThP}{\ExternalLink}} \footnotesize Example of using \cpp{qsort} to sort an array of integers. \tcblower \cppfile{code_examples/algorithms/qsort_code.h} -\end{box-note} +\end{codebox} -I would strongly recommend avoiding \cpp{qsort}, as \cpp{std::sort} and \cpp{std::ranges::sort} should be a better choice in every situation. Moreover, \cpp{qsort} is only valid for trivially copyable types, and those will correctly optimize to \cpp{memcpy}/\cpp{memmove} operations even when using \cpp{std::sort}. +I would strongly recommend avoiding \cpp{qsort}, as \cpp{std::sort} and \cpp{std::ranges::sort} should be a better choice in every situation. Moreover, \cpp{qsort} is only valid for trivially copyable types, and those will correctly optimize to \cpp{memcpy} / \cpp{memmove} operations even when using \cpp{std::sort}. -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/nT9GPh8Tq}{\ExternalLink}} \footnotesize Example of using \cpp{std::sort} to achieve the same result as in the previous example. \tcblower \cppfile{code_examples/algorithms/qsort_not_code.h} -\end{box-nobreak} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_04_partitioning.tex b/chapters/03_algorithms_04_partitioning.tex index 26460ce..4d0b1a4 100644 --- a/chapters/03_algorithms_04_partitioning.tex +++ b/chapters/03_algorithms_04_partitioning.tex @@ -12,11 +12,11 @@ \subsection{\texorpdfstring{\cpp{std::partition}}{\texttt{std::partition}}} \cppversions{\texttt{partition}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/9P8eWKfsd}{\ExternalLink}} \footnotesize Example of using \cpp{std::partition} to process exam results. \tcblower \cppfile{code_examples/algorithms/partition_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::stable_partition}}{\texttt{std::stable\_partition}}} \index{\cpp{std::stable_partition}} @@ -29,11 +29,11 @@ \subsection{\texorpdfstring{\cpp{std::stable_partition}}{\texttt{std::stable\_pa \constraints{\texttt{bidirectional\_range}}{\texttt{bidirectional\_range}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/zjeczYqK8}{\ExternalLink}} \footnotesize Example of using \cpp{std::stable_partition} to move selected items to the beginning of a list. \tcblower \cppfile{code_examples/algorithms/stable_partition_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_partitioned}}{\texttt{std::is\_partitioned}}} \index{\cpp{std::is_partitioned}} @@ -45,11 +45,11 @@ \subsection{\texorpdfstring{\cpp{std::is_partitioned}}{\texttt{std::is\_partitio Note that a sorted range is always partitioned for any possible value (with a different partition point). -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/nKecEhs3b}{\ExternalLink}} \footnotesize Example of using \cpp{std::is_partitioned}. \tcblower \cppfile{code_examples/algorithms/is_partitioned_code.h} -\end{box-nobreak} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::partition_copy}}{\texttt{std::partition\_copy}}} \index{\cpp{std::partition_copy}} @@ -59,11 +59,11 @@ \subsection{\texorpdfstring{\cpp{std::partition_copy}}{\texttt{std::partition\_c \cppversions{\texttt{partition\_copy}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{input\_range -> (output\_iterator, output\_iterator)}}{\texttt{forward\_range -> (forward\_iterator, forward\_iterator)}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/nzrbczh4j}{\ExternalLink}} \footnotesize Example of using \cpp{std::partition_copy} to copy even elements into one range and odd elements into another range. \tcblower \cppfile{code_examples/algorithms/partition_copy_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::nth_element}}{\texttt{std::nth\_element}}} \index{\cpp{std::nth_element}} @@ -80,8 +80,8 @@ \subsection{\texorpdfstring{\cpp{std::nth_element}}{\texttt{std::nth\_element}}} However, note that the standard only mandates average $O(n)$ complexity, and \cpp{std::nth_element} implementations can have high overhead, so always test to determine which provides better performance for your use case. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/n7T3h5sM8}{\ExternalLink}} \footnotesize Example of using \cpp{std::nth_element}. \tcblower \cppfile{code_examples/algorithms/nth_element_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index c865974..2fc9309 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -24,19 +24,19 @@ \subsection{\texorpdfstring{\cpp{std::lower_bound}, \cpp{std::upper_bound}}{\tex \item if no such element exists, both algorithms return the end iterator \end{itemize} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/cbz464eWe}{\ExternalLink}} \footnotesize Example of using \cpp{std::lower_bound} and \cpp{std::upper_bound} to divide a sorted range into three parts: lower than the bottom threshold, between the bottom and upper threshold and higher than the upper threshold. \tcblower \cppfile{code_examples/algorithms/lower_bound_code.h} -\end{box-note} +\end{codebox} While the algorithms will operate on any \cpp{forward_range}, the logarithmic divide and conquer behaviour is only available for \cpp{random_access_range}. Data structures like \cpp{std::set}, \cpp{std::multiset}, \cpp{std::map} and \cpp{std::multimap} offer their $O(logn)$ implementations of lower and upper bound as methods. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/o9Wdvzno9}{\ExternalLink}} \footnotesize Example of using \cpp{lower_bound} and \cpp{upper_bound} methods on a \cpp{std::multiset}. \tcblower \cppfile{code_examples/algorithms/lower_bound_set_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::equal_range}}{\texttt{std::equal\_range}}} \index{\cpp{std::equal_range}} @@ -48,11 +48,11 @@ \subsection{\texorpdfstring{\cpp{std::equal_range}}{\texttt{std::equal\_range}}} Because the lower bound returns the first element for which \texttt{elem >= value} and the upper bound returns the first element for which \texttt{value < elem}, the result is a range \texttt{[lb, ub)} of elements equal to the value. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/bhW73YG1b}{\ExternalLink}} \footnotesize Example of using \cpp{std::equal_range}. \tcblower \cppfile{code_examples/algorithms/equal_range_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::partition_point}}{\texttt{std::partition\_point}}} \index{\cpp{std::partition_point}} @@ -64,11 +64,11 @@ \subsection{\texorpdfstring{\cpp{std::partition_point}}{\texttt{std::partition\_ \cpp{std::partition_point} will return the first element that does not satisfy the provided predicate. This algorithm only requires the range to be partitioned (with respect to the predicate). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/WTzs4zYTq}{\ExternalLink}} \footnotesize Example of using \cpp{std::partition_point}. \tcblower \cppfile{code_examples/algorithms/partition_point_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::binary_search}}{\texttt{std::binary\_search}}} \index{\cpp{std::binary_search}} @@ -80,27 +80,27 @@ \subsection{\texorpdfstring{\cpp{std::binary_search}}{\texttt{std::binary\_searc Using \cpp{std::binary_search} is equivalent to calling \cpp{std::equal_range} and checking whether the returned is non-empty; however, \cpp{std::binary_search} offers a single lookup performance, where \cpp{std::equal_range} does two lookups to determine the lower and upper bounds. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7fP71Kjvx}{\ExternalLink}} \footnotesize Example of using \cpp{std::binary_search} with an equivalent check using \cpp{std::equal_range}. \tcblower \cppfile{code_examples/algorithms/binary_search_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{bsearch} - C standard library}{\texttt{bsearch} - C standard library}} \index{\cpp{bsearch}} From the C standard library, C++ inherits \cpp{bsearch}. This algorithm returns one of the elements equal to the provided key, or \cpp{nullptr} if none such element is found. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/sEEashheY}{\ExternalLink}} \footnotesize Example of using \cpp{bsearch}. \tcblower \cppfile{code_examples/algorithms/bsearch_code.h} -\end{box-note} +\end{codebox} As with \cpp{qsort}, there is effectively no reason to use \cpp{bsearch} in C++ code. Depending on the specific use case, one of the previously mentioned algorithms should be a suitable replacement. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/9ePnd7vGc}{\ExternalLink}} \footnotesize Example demonstrating alternatives to \cpp{bsearch}. \tcblower \cppfile{code_examples/algorithms/bsearch_alternatives_code.h} -\end{box-note} +\end{codebox} diff --git a/chapters/03_algorithms_06_linear_sorted.tex b/chapters/03_algorithms_06_linear_sorted.tex index ffbd795..d8c8a29 100644 --- a/chapters/03_algorithms_06_linear_sorted.tex +++ b/chapters/03_algorithms_06_linear_sorted.tex @@ -10,11 +10,11 @@ \subsection{\texorpdfstring{\cpp{std::includes}}{\texttt{std::includes}}} \cppversions{\texttt{includes}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range)}}{\texttt{(forward\_range, forward\_range)}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/od9oa6xEP}{\ExternalLink}} \footnotesize Example of using \cpp{std::includes} to check whether a string contains all English (lower-case) letters. \tcblower \cppfile{code_examples/algorithms/includes_code.h} -\end{box-note} +\end{codebox} The example uses \cpp{std::iota} to generate the lower-case letters (line 2), which requires the destination vector to be pre-allocated (line 1). @@ -28,19 +28,19 @@ \subsection{\texorpdfstring{\cpp{std::merge}}{\texttt{std::merge}}} The merge operation is stable. Equal elements from the first range will be ordered before equal elements from the second range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/fj3rzovKf}{\ExternalLink}} \footnotesize Example of using \cpp{std::merge}. \tcblower \cppfile{code_examples/algorithms/merge_code.h} -\end{box-note} +\end{codebox} The parallel version requires the output to be a forward range (represented by a \cpp{forward_iterator}). Therefore, we cannot use wrappers like \cpp{std::back_inserter} and must preallocate the output range to sufficient capacity. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/oEPTe7GnY}{\ExternalLink}} \footnotesize Example of parallel \cpp{std::merge}. \tcblower \cppfile{code_examples/algorithms/merge_par_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::inplace_merge}}{\texttt{std::inplace\_merge}}} \index{\cpp{std::inplace_merge}} @@ -52,11 +52,11 @@ \subsection{\texorpdfstring{\cpp{std::inplace_merge}}{\texttt{std::inplace\_merg When using the iterator-based interface, the middle iterator (i.e. the iterator to the first element of the second sub-range) is the second argument. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/8Ge81Gojr}{\ExternalLink}} \footnotesize Example of using \cpp{std::inplace_merge}. \tcblower \cppfile{code_examples/algorithms/inplace_merge_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{std::unique}, \texttt{std::unique\_copy}}} \index{\cpp{std::unique}} @@ -69,19 +69,19 @@ \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{s Because unique works in-place and cannot resize the underlying range, it leaves the end of the range with unspecified values and returns an iterator to the beginning of this sub-range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7bcKTfe9b}{\ExternalLink}} \footnotesize Example of using \cpp{std::unique}. \tcblower \cppfile{code_examples/algorithms/unique_code.h} -\end{box-note} +\end{codebox} The \cpp{std::unique_copy} is a variant of \cpp{std::unique} that outputs the unique values to a second range. \cppversions{\texttt{unique\_copy}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TMP7ec6P5}{\ExternalLink}} \footnotesize Example of using \cpp{std::unique_copy}. \tcblower \cppfile{code_examples/algorithms/unique_copy_code.h} -\end{box-note} +\end{codebox} diff --git a/chapters/03_algorithms_07_sets.tex b/chapters/03_algorithms_07_sets.tex index e567d8e..2393957 100644 --- a/chapters/03_algorithms_07_sets.tex +++ b/chapters/03_algorithms_07_sets.tex @@ -10,19 +10,19 @@ \subsection{\texorpdfstring{\cpp{std::set_difference}}{\texttt{std::set\_differe \cppversions{\texttt{set\_difference}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range) -> output\_iterator}}{\texttt{(forward\_range, forward\_range) -> forward\_iterator}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::max(M-N, 0)} such elements from the first range. - -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/WMz45WbW6}{\ExternalLink}} \footnotesize Example of using \cpp{std::set_difference}. \tcblower \cppfile{code_examples/algorithms/set_difference_code.h} -\end{box-note} +\end{codebox} + +For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::max(M-N,0)} such elements from the first range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/YdPnxPh88}{\ExternalLink}} \footnotesize Example demonstrating \cpp{std::set_difference} behaviour when equivalent elements are present. \tcblower \cppfile{code_examples/algorithms/set_difference_equal_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::set_symmetric_difference}}{\texttt{std::set\_symmetric\_difference}}} \index{\cpp{std::set_symmetric_difference}} @@ -32,19 +32,19 @@ \subsection{\texorpdfstring{\cpp{std::set_symmetric_difference}}{\texttt{std::se \cppversions{\texttt{set\_sym\dots diff\dots}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range) -> output\_iterator}}{\texttt{(forward\_range, forward\_range) -> forward\_iterator}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::abs(M-N)} such elements from the corresponding range. That is, if $M>N$, $M-N$ elements will be copied from the first range; otherwise, $N-M$ elements will be copied from the second range. +\begin{codebox}[]{\href{https://compiler-explorer.com/z/PrTn6voGn}{\ExternalLink}} + \footnotesize Example of using \cpp{std::set_symmetric_difference}. + \tcblower + \cppfile{code_examples/algorithms/set_symmetric_difference_code.h} +\end{codebox} -\begin{box-nobreak} -\footnotesize Example of using \cpp{std::set_symmetric_difference}. -\tcblower -\cppfile{code_examples/algorithms/set_symmetric_difference_code.h} -\end{box-nobreak} +For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::abs(M-N)} such elements from the corresponding range. That is, if $M>N$, $M-N$ elements will be copied from the first range; otherwise, $N-M$ elements will be copied from the second range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7hGacnfWe}{\ExternalLink}} \footnotesize Example demonstrating \cpp{std::set_symmetric_difference} behaviour when equivalent elements are present. \tcblower \cppfile{code_examples/algorithms/set_symmetric_difference_equal_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::set_union}}{\texttt{std::set\_union}}} \index{\cpp{std::set_union}} @@ -54,19 +54,19 @@ \subsection{\texorpdfstring{\cpp{std::set_union}}{\texttt{std::set\_union}}} \cppversions{\texttt{set\_union}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range) -> output\_iterator}}{\texttt{(forward\_range, forward\_range) -> forward\_iterator}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain $M$ elements from the first range, followed by the last \cpp{std::max(N-M,0)} elements from the second range. +\begin{codebox}[]{\href{https://compiler-explorer.com/z/anvj66jfT}{\ExternalLink}} + \footnotesize Example of using \cpp{std::set_union}. + \tcblower + \cppfile{code_examples/algorithms/set_union_code.h} +\end{codebox} -\begin{box-note} -\footnotesize Example of using \cpp{std::set_union}. -\tcblower -\cppfile{code_examples/algorithms/set_union_code.h} -\end{box-note} +For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain $M$ elements from the first range, followed by the last \cpp{std::max(N-M,0)} elements from the second range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/KbqGWbzP6}{\ExternalLink}} \footnotesize Example demonstrating \cpp{std::set_union} behaviour when equivalent elements are present. \tcblower \cppfile{code_examples/algorithms/set_union_equal_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::set_intersection}}{\texttt{std::set\_intersection}}} \index{\cpp{std::set_intersection}} @@ -76,16 +76,16 @@ \subsection{\texorpdfstring{\cpp{std::set_intersection}}{\texttt{std::set\_inter \cppversions{\texttt{set\_intersection}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range) -> output\_iterator}}{\texttt{(forward\_range, forward\_range) -> forward\_iterator}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the first \cpp{std::min(M,N)} elements from the first range. +\begin{codebox}[]{\href{https://compiler-explorer.com/z/h3fqn9K6v}{\ExternalLink}} + \footnotesize Example of using \cpp{std::set_intersection}. + \tcblower + \cppfile{code_examples/algorithms/set_intersection_code.h} +\end{codebox} -\begin{box-note} -\footnotesize Example of using \cpp{std::set_intersection}. -\tcblower -\cppfile{code_examples/algorithms/set_intersection_code.h} -\end{box-note} +For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the first \cpp{std::min(M,N)} elements from the first range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/seK3YWqTo}{\ExternalLink}} \footnotesize Example demonstrating \cpp{std::set_intersection} behaviour when equivalent elements are present. \tcblower \cppfile{code_examples/algorithms/set_intersection_equal_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_08_transformations.tex b/chapters/03_algorithms_08_transformations.tex index 87b632a..141fb62 100644 --- a/chapters/03_algorithms_08_transformations.tex +++ b/chapters/03_algorithms_08_transformations.tex @@ -9,42 +9,16 @@ \subsection{\texorpdfstring{\cpp{std::transform}}{\texttt{std::transform}}} \cppversions{\texttt{transform}}{\CC98}{\CC20}{\CC17}{\CC20} -% Mention lazy version - \constraints{\texttt{input\_range -> output\_iterator}\newline\texttt{(input\_range, input\_iterator) -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}\newline\texttt{(forward\_range, forward\_iterator) -> forward\_iterator}}{N/A}{\texttt{unary\_functor}\newline\texttt{binary\_functor}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6W38nMh6b}{\ExternalLink}} \footnotesize Example of unary and binary version of \cpp{std::transform}. Note that the output iterator can be one of the input ranges' begin iterator (line 4 and 12). \tcblower \cppfile{code_examples/algorithms/transform_code.h} -\end{box-note} +\end{codebox} Note that \cpp{std::transform} does not guarantee strict left-to-right evaluation. If that is required, use \cpp{std::for_each} instead. -\subsection{\texorpdfstring{\cpp{std::adjacent_difference}}{\texttt{std::adjacent\_difference}}} -\index{\cpp{std::adjacent_difference}} - -The \cpp{std::adjacent_difference} is a numerical algorithm, which might make it seem out of place in this section; however, it is essentially a binary transformation algorithm that operates on adjacent elements of the single source range. - -Unlike \cpp{std::transform}, \cpp{std::adjacent_difference} guarantees left-to-right application. Furthermore, because it operates on input ranges, the algorithm internally stores a copy of the last read value for use as the left operand in the next step. - -\cppversions{\texttt{adjacent\_difference}}{\CC98}{\CC20}{\CC17}{N/A} -\constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{operator -}}{\texttt{binary\_functor}} - -\begin{box-note} -\footnotesize Example of the default version of \cpp{std::adjacent_difference}, which will calculate the difference of adjacent elements, with the first element copied. -\tcblower -\cppfile{code_examples/algorithms/adjacent_difference_code.h} -\end{box-note} - -\begin{box-note} -\footnotesize Example of more inventive use of \cpp{std::adjacent_difference} to generate the Fibonacci sequence. -\tcblower -\cppfile{code_examples/algorithms/adjacent_difference_extra_code.h} -\end{box-note} - -Note that the \cpp{std::next(data.begin())} for the output range is critical here. The \cpp{std::adjacent_difference} algorithm will read each element only once and remembers the previously read value for the left argument. The \cpp{std::next} ensures that we generate one element ahead of either argument. - \subsection{\texorpdfstring{\cpp{std::remove}, \cpp{std::remove_if}}{\texttt{std::remove}, \texttt{std::remove\_if}}} \index{\cpp{std::remove}} \index{\cpp{std::remove_if}} @@ -56,11 +30,11 @@ \subsection{\texorpdfstring{\cpp{std::remove}, \cpp{std::remove_if}}{\texttt{std \cppversions{\texttt{remove, remove\_if}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/eb56eshTq}{\ExternalLink}} \footnotesize Example of using \cpp{std::remove} and \cpp{std::remove_if}. \tcblower \cppfile{code_examples/algorithms/remove_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::replace}, \cpp{std::replace_if}}{\texttt{std::replace}, \texttt{std::replace\_if}}} \index{\cpp{std::replace}} @@ -71,11 +45,11 @@ \subsection{\texorpdfstring{\cpp{std::replace}, \cpp{std::replace_if}}{\texttt{s \cppversions{\texttt{replace, replace\_if}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/aPrWhxxsj}{\ExternalLink}} \footnotesize Example of using \cpp{std::replace} and \cpp{std::replace_if}. \tcblower \cppfile{code_examples/algorithms/replace_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} \index{\cpp{std::reverse}} @@ -87,19 +61,19 @@ \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} Note that using \cpp{std::reverse} is only a reasonable solution if the range must be mutated because bidirectional ranges already support reverse iteration. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/onsj6dvqK}{\ExternalLink}} \footnotesize Example of using \cpp{std::reverse} and reverse iteration, provided by bidirectional ranges. \tcblower \cppfile{code_examples/algorithms/reverse_code.h} -\end{box-note} +\end{codebox} C-style arrays and C-style strings can be adapted using \cpp{std::span} and \texttt{std::string\-\_view} to allow reverse iteration. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/YW8sEa4c3}{\ExternalLink}} \footnotesize Example of using \cpp{std::span} and \cpp{std::string_view} to addapt C-style constructs for reverse iteration. \tcblower \cppfile{code_examples/extras/span_stringview_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} \index{\cpp{std::rotate}} @@ -109,11 +83,36 @@ \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} \cppversions{\texttt{rotate}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/dP7TMfjKv}{\ExternalLink}} \footnotesize Example of using \cpp{std::rotate}. \tcblower \cppfile{code_examples/algorithms/rotate_code.h} -\end{box-note} +\end{codebox} + +\subsection{\texorpdfstring{\cpp{std::shift_left}, \cpp{std::shift_right}}{\texttt{std::shift\_left}, \texttt{std::shift\_right}}} +\index{\cpp{std::shift_left}} +\index{\cpp{std::shift_right}} + +The \cpp{std::shift_left} and \cpp{std::shift_right} algorithms move elements in the provided range by the specified amount of positions. +However, unlike std::rotate, the shift doesn't wrap around. + +\cppversions{\texttt{shift\_left}}{\CC20}{\CC20}{\CC20}{\CC20} +\cppversions{\texttt{shift\_right}}{\CC20}{\CC20}{\CC20}{\CC20} +\constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{}{} + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/5Tjh3M5Kv}{\ExternalLink}} +\footnotesize Example of using \cpp{std::shift_left} and \cpp{std::shift_right} with a trivially copyable type. +\tcblower +\cppfile{code_examples/algorithms/shift_code.h} +\end{codebox} + +One way to think about these algorithms is that they "make space" for the requested number of new elements. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/sbY3fxYrs}{\ExternalLink}} +\footnotesize Example of using \cpp{std::shift_right} to make space for four new elements. Note that \texttt{'d'} wasn't moved as there is no place to move it to, and in the context of "making space" will be overwritten anyway. +\tcblower +\cppfile{code_examples/algorithms/shift_move_code.h} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::shuffle}}{\texttt{std::shuffle}}} \index{\cpp{std::shuffle}} @@ -123,11 +122,11 @@ \subsection{\texorpdfstring{\cpp{std::shuffle}}{\texttt{std::shuffle}}} \cppversions{\texttt{shuffle}}{\CC11}{N/A}{N/A}{\CC20} \constraints{\texttt{random\_access\_range}}{}{}{} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/PE3bM8Gof}{\ExternalLink}} \footnotesize Example of using \cpp{std::shuffle} to shuffle a deck of cards. \tcblower \cppfile{code_examples/algorithms/shuffle_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::next_permutation}, \cpp{std::prev_permutation}}{\texttt{std::next\_permutation}, \texttt{std::prev\_permutation}}} \index{\cpp{std::next_permutation}} @@ -139,11 +138,11 @@ \subsection{\texorpdfstring{\cpp{std::next_permutation}, \cpp{std::prev_permutat \cppversions{\texttt{prev\_permutation}}{\CC98}{\CC20}{N/A}{\CC20} \constraints{\texttt{bidirectional\_range}}{}{\texttt{operator <}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/shfGz3brv}{\ExternalLink}} \footnotesize Example of using \cpp{std::next_permutation} to iterate over all permutations of three unique elements. \tcblower \cppfile{code_examples/algorithms/next_permutation_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_permutation}}{\texttt{std::is\_permutation}}} \index{\cpp{std::is_permutation}} @@ -153,8 +152,8 @@ \subsection{\texorpdfstring{\cpp{std::is_permutation}}{\texttt{std::is\_permutat \cppversions{\texttt{is\_permutation}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_range)}}{\texttt{(forward\_range, forward\_range)}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/axqfP6aaT}{\ExternalLink}} \footnotesize Example of using \cpp{std::is_permutation} to validate a simple sort implementation. \tcblower \cppfile{code_examples/algorithms/is_permutation_code.h} -\end{box-note} +\end{codebox} diff --git a/chapters/03_algorithms_09_left_folds.tex b/chapters/03_algorithms_09_left_folds.tex index 61c0da8..d9dc450 100644 --- a/chapters/03_algorithms_09_left_folds.tex +++ b/chapters/03_algorithms_09_left_folds.tex @@ -1,9 +1,13 @@ \section{Left folds} -Left folds are one of the two big groups of numerical algorithms. Folds operate in a strict order, "folding in" one element at a time by evaluating \cpp{acc = fold_op(acc, el)} for each element. For left folds, the direction of operation is left to right. However, we can easily change the direction for bidirectional ranges since those support reverse iteration. +Left folds are one of the two big groups of numerical algorithms. +Folds operate in a strict order, "folding in" one element at a time by evaluating \cpp{acc=fold_op(acc,el)} for each element. +For left folds, the direction of operation is left to right. Because of the strictly linear operation, none of the left-fold algorithms supports a parallel version. +When using numerical algorithms, it's worth understanding the behaviour of numerical types in C++, notably the interactions between silent implicit conversions and template type deduction rules. You can read more on this in the theory chapter in the section \ref{theory:numerics}. + \subsection{\texorpdfstring{\cpp{std::accumulate}}{\texttt{std::accumulate}}} \index{\cpp{std::accumulate}} @@ -12,20 +16,19 @@ \subsection{\texorpdfstring{\cpp{std::accumulate}}{\texttt{std::accumulate}}} \cppversions{\texttt{accumulate}}{\CC98}{\CC20}{N/A}{N/A} \constraints{\texttt{input\_range}}{}{\texttt{operator +}}{\texttt{binary\_functor}} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/3decPenW6}{\ExternalLink}} \footnotesize Example of using \cpp{std::accumulate}. \tcblower \cppfile{code_examples/algorithms/accumulate_code.h} -\end{box-note} +\end{codebox} -Note that the algorithm accepts an initial value that also dictates the accumulator type. -% TODO: link to chapter about numerical conversions and promotions +While left folds operate strictly left-to-right, we can mitigate this limitation by utilizing reverse iterators—note, of course, that this requires at least a bidirectional range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6verMGo6P}{\ExternalLink}} \footnotesize Example of using \cpp{std::accumulate} as a right fold. \tcblower \cppfile{code_examples/algorithms/accumulate_right_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::inner_product}}{\texttt{std::inner\_product}}} \index{\cpp{std::inner_product}} @@ -37,21 +40,21 @@ \subsection{\texorpdfstring{\cpp{std::inner_product}}{\texttt{std::inner\_produc The default version uses \cpp{operator*} for the reduction and \cpp{operator+} for the fold operation. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/j9M7eGxnd}{\ExternalLink}} \footnotesize Example of using \cpp{std::inner_product}. \tcblower \cppfile{code_examples/algorithms/inner_product_code.h} -\end{box-note} +\end{codebox} Because the algorithm only uses the ranges as input, there is no issue with overlapping ranges. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1G6Yc3rhn}{\ExternalLink}} \footnotesize Example of using \cpp{std::inner_product} on a single range to calculate sum of absolute differences between elements. \tcblower \cppfile{code_examples/algorithms/inner_product_one_code.h} -\end{box-note} +\end{codebox} -\subsection{\texorpdfstring{\cpp{std::partial_sum}}{\texttt{std::partial_sum}}} +\subsection{\texorpdfstring{\cpp{std::partial_sum}}{\texttt{std::partial\_sum}}} \index{\cpp{std::partial_sum}} The \cpp{std::partial_sum} algorithm operates as a left fold. However, it doesn't reduce the range into a single value. Instead, the algorithm writes each partial result to the output range. @@ -59,12 +62,44 @@ \subsection{\texorpdfstring{\cpp{std::partial_sum}}{\texttt{std::partial_sum}}} \cppversions{\texttt{partial\_sum}}{\CC98}{\CC20}{N/A}{N/A} \constraints{\texttt{input\_range -> output\_iterator}}{}{\texttt{operator+}}{\texttt{binary\_functor}} -The output iterator is permitted to be the input ranges’ begin iterator. +The output iterator is permitted to be the input ranges' begin iterator. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/G83s3edax}{\ExternalLink}} \footnotesize Example of using \cpp{std::partial_sum}. \tcblower \cppfile{code_examples/algorithms/partial_sum_code.h} -\end{box-note} +\end{codebox} + +\subsection{\texorpdfstring{\cpp{std::adjacent_difference}}{\texttt{std::adjacent\_difference}}} +\index{\cpp{std::adjacent_difference}} + +The \cpp{std::adjacent_difference} is a numerical algorithm, which is the odd one out as it provides both a strict left-to-right variant, but at the same time also supports parallel execution, where it behaves like a generalized reduction (we will talk about those in the next section). + +The algorithm operates similarly to the \cpp{std::transform} algorithm, operating on each pair of consecutive elements in the range. However, unlike \cpp{std::transform}, it does guarantee left-to-right execution. -% TODO: stateful functors \ No newline at end of file +The algorithm also supports input ranges, which is achieved by internally storing the copy of the last right operand to be reused as the left operand for the subsequent reduction step. + +\cppversions{\texttt{adjacent\_difference}}{\CC98}{\CC20}{\CC17}{N/A} +\constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{operator -}}{\texttt{binary\_functor}} + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6vPea76qr}{\ExternalLink}} +\footnotesize Example of the default version of \cpp{std::adjacent_difference}, which will calculate the difference of adjacent elements, with the first element copied. +\tcblower +\cppfile{code_examples/algorithms/adjacent_difference_code.h} +\end{codebox} + +The left-to-right operation can be exploited for generative use cases. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/fn18zEP4e}{\ExternalLink}} +\footnotesize Example of more inventive use of \cpp{std::adjacent_difference} to generate the Fibonacci sequence. +\tcblower +\cppfile{code_examples/algorithms/adjacent_difference_extra_code.h} +\end{codebox} + +For parallel overloads, the input and output ranges cannot overlap. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7hYj7qaTb}{\ExternalLink}} +\footnotesize Example of the parallel \cpp{std::adjacent_difference} overload. +\tcblower +\cppfile{code_examples/algorithms/adjacent_difference_par_code.h} +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_10_general_reductions.tex b/chapters/03_algorithms_10_general_reductions.tex index 398abe6..6c9bff3 100644 --- a/chapters/03_algorithms_10_general_reductions.tex +++ b/chapters/03_algorithms_10_general_reductions.tex @@ -16,36 +16,36 @@ \subsection{\texorpdfstring{\cpp{std::reduce}}{\texttt{std::reduce}}} Note that while we have access to a sequenced execution policy (i.e. \newline\cpp{std::execution::seq}), this does not make \cpp{std::reduce} sequenced in a left-fold sense. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/qxWsoM4Mf}{\ExternalLink}} \footnotesize Example of using \cpp{std::reduce} with and without an execution policy. \tcblower \cppfile{code_examples/algorithms/reduce_code.h} -\end{box-note} +\end{codebox} On top of the \cpp{std::accumulate} equivalents, we get one more overload that does away with the initial accumulator value, which removes the potential for using the wrong literal. Instead, the accumulator will be of the type of the ranges’ elements and will be value initialised. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/5PoEcEvd7}{\ExternalLink}} \footnotesize Example of using \cpp{std::reduce} without specifying the initial value of the accumulator. \tcblower \cppfile{code_examples/algorithms/reduce_noinit_code.h} -\end{box-note} +\end{codebox} The initial value of the accumulator will be “Quack” (line 2). Adding the other two ducks (line 8), we end up with “QuackQuackQuack”. \subsection{\texorpdfstring{\cpp{std::transform_reduce}}{\texttt{std::transform\_reduce}}} \index{\cpp{std::transform_reduce}} -The \cpp{std::transform_reduce} algorithm is the generalised counterpart to \texttt{std::inner\-\_product}. On top of the two-range variant, the algorithm also provides a unary overload. +The \cpp{std::transform_reduce} algorithm is the generalised counterpart to \linebreak\cpp{std::inner_product}. On top of the two-range variant, the algorithm also provides a unary overload. \cppversions{\texttt{transform\_reduce}}{\CC17}{\CC20}{\CC17}{N/A} \constraints{\texttt{input\_range}}{\texttt{forward\_range}}{N/A}{\texttt{(binary\_functor, unary\_functor)}} \constraints{\texttt{(input\_range, input\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{\texttt{(std::plus<>(), std::multiplies<>())}}{\texttt{(binary\_functor, binary\_functor)}} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/P8ohM19bW}{\ExternalLink}} \footnotesize Example of using the unary version of \cpp{std::transform_reduce} to calculate a sum of squares and the binary version to calculate a sum of elements multiplied by coefficients. \tcblower \cppfile{code_examples/algorithms/transform_reduce_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::inclusive_scan}, \cpp{std::exclusive_scan}}{\texttt{std::inclusive\_scan}, \texttt{std::exclusive\_scan}}} \index{\cpp{std::inclusive_scan}} @@ -60,19 +60,19 @@ \subsection{\texorpdfstring{\cpp{std::inclusive_scan}, \cpp{std::exclusive_scan} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{std::plus<>()}}{\texttt{binary\_functor}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/3hKaoczE3}{\ExternalLink}} \footnotesize Example of using \cpp{std::inclusive_scan}. \tcblower \cppfile{code_examples/algorithms/inclusive_scan_code.h} -\end{box-note} +\end{codebox} Consequently, because the first element generated by \cpp{std::exclusive_scan} is the sum of zero elements, we must specify an initial value of the accumulator, which will be the value of the first generated element. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/eEc1PTv8G}{\ExternalLink}} \footnotesize Example of using \cpp{std::exclusive_scan}. \tcblower \cppfile{code_examples/algorithms/exclusive_scan_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::transform_inclusive_scan},\newline\cpp{std::transform_exclusive_scan}}{\texttt{std::transform\_inclusive\_scan},\newline \texttt{std::transform\_exclusive\_scan}}} \index{\cpp{std::transform_inclusive_scan}} @@ -84,8 +84,8 @@ \subsection{\texorpdfstring{\cpp{std::transform_inclusive_scan},\newline\cpp{std \cppversions{\texttt{transform\_exclu\dots}}{\CC17}{\CC20}{\CC17}{N/A} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{N/A}{\texttt{(binary\_functor, unary\_functor)}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/oxdjMjY1c}{\ExternalLink}} \footnotesize Example of using \cpp{std::transform_inclusive_scan} to accumulate absolute values of elements. \tcblower \cppfile{code_examples/algorithms/transform_inclusive_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_11_boolean.tex b/chapters/03_algorithms_11_boolean.tex index f0d1024..2490a8e 100644 --- a/chapters/03_algorithms_11_boolean.tex +++ b/chapters/03_algorithms_11_boolean.tex @@ -35,9 +35,8 @@ \subsection{\texorpdfstring{\cpp{std::all_of}, \cpp{std::any_of}, \cpp{std::none \end{tabular} \end{center} - -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Kf1319e3j}{\ExternalLink}} \footnotesize Example demonstrating all three boolean reduction algorithms. \tcblower \cppfile{code_examples/algorithms/all_of_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_12_generators.tex b/chapters/03_algorithms_12_generators.tex index 89c2a15..f39c298 100644 --- a/chapters/03_algorithms_12_generators.tex +++ b/chapters/03_algorithms_12_generators.tex @@ -15,26 +15,26 @@ \subsection{\texorpdfstring{\cpp{std::fill}, \cpp{std::generate}}{\texttt{std::f The generator provided to \cpp{std::generate} can be a non-regular function since \cpp{std::generate} guarantees strict left-to-right evaluation. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6dnTG7Mqo}{\ExternalLink}} \footnotesize Example of using \cpp{std::fill} and \cpp{std::generate}. \tcblower \cppfile{code_examples/algorithms/generate_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::fill_n}, \cpp{std::generate_n}}{\texttt{std::fill\_n}, \texttt{std::generate\_n}}} \index{\cpp{std::fill_n}} \index{\cpp{std::generate_n}} -The \cpp{std::fill_n} and \cpp{std::generate_n} are variants of \cpp{std::fill} and \cpp{std::generate} that operate on ranges specified using the start iterator and number of elements. This behaviour allows the algorithms to be used with iterator adapters, such as \cpp{std::back_inserter}. +The \cpp{std::fill_n} and \cpp{std::generate_n} are variants of \cpp{std::fill} and \newline\cpp{std::generate} that operate on ranges specified using the start iterator and number of elements. This behaviour allows the algorithms to be used with iterator adapters, such as \cpp{std::back_inserter}. \cppversions{\texttt{fill\_n, generate\_n}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{output\_iterator}}{\texttt{forward\_iterator}}{N/A}{\texttt{generator}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/qGcdezEaG}{\ExternalLink}} \footnotesize Example of using \cpp{std::fill_n} and \cpp{std::generate_n}. \tcblower \cppfile{code_examples/algorithms/fill_n_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::iota}}{\texttt{std::iota}}} \index{\cpp{std::iota}} @@ -44,18 +44,18 @@ \subsection{\texorpdfstring{\cpp{std::iota}}{\texttt{std::iota}}} \cppversions{\texttt{iota}}{\CC11}{\CC20}{N/A}{\CC23} \constraints{\texttt{forward\_range}}{}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/19WaoodvM}{\ExternalLink}} \footnotesize Example of using \cpp{std::iota}. \tcblower \cppfile{code_examples/algorithms/iota_code.h} -\end{box-note} +\end{codebox} Notably, the \cpp{std::iota} algorithm is also an outlier in the support added with the C++20 standard. The \cpp{std::iota} algorithm did not receive a range version. However, we do have access to an iota view. -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/qnz9hWxh8}{\ExternalLink}} \footnotesize Example of using both finite and infinite \cpp{std::views::iota}. \tcblower \cppfile{code_examples/algorithms/iota_view_code.h} -\end{box-nobreak} +\end{codebox} Here we take advantage of the finite view constructor \cpp{std::views::iota(1,10)} to establish the output size (line 3), which allows us to use the infinite view \texttt{std::views\-::iota(5)} for the second parameter. Functionally, we could swap even the second view for a finite one. However, this would impose an additional (and unnecessary) boundary check. diff --git a/chapters/03_algorithms_13_copies_moves.tex b/chapters/03_algorithms_13_copies_moves.tex index 2b2e5bd..6e6534d 100644 --- a/chapters/03_algorithms_13_copies_moves.tex +++ b/chapters/03_algorithms_13_copies_moves.tex @@ -15,27 +15,27 @@ \subsection{\texorpdfstring{\cpp{std::copy}, \cpp{std::move}}{\texttt{std::copy} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/d5c855Gcv}{\ExternalLink}} \footnotesize Example of a non-overlapping and permitted overlapping case of \cpp{std::copy}. \tcblower \cppfile{code_examples/algorithms/copy_code.h} -\end{box-note} +\end{codebox} Move operates identically, except it casts each element to an rvalue before the assignment, turning copies into moves. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/cP1P3e6q3}{\ExternalLink}} \footnotesize Example of using \cpp{std::move}. \tcblower \cppfile{code_examples/algorithms/move_code.h} -\end{box-note} +\end{codebox} Significantly, whether std::move will move depends on the underlying element type. If the underlying type is copy-only, std::move will behave identically to std::copy. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/9ejzxEr8K}{\ExternalLink}} \footnotesize Example of using \cpp{std::move} with a copy-only type. \tcblower \cppfile{code_examples/algorithms/move_nomove_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::copy_backward}, \cpp{std::move_backward}}{\texttt{std::copy\_backward}, \texttt{std::move\_backward}}} \index{\cpp{std::copy_backward}} @@ -50,11 +50,11 @@ \subsection{\texorpdfstring{\cpp{std::copy_backward}, \cpp{std::move_backward}}{ The output iterator cannot be within \cpp{(first, last]} and will be treated as the end iterator for the destination range, meaning that the algorithm will write the first value to \cpp{std::prev(end)}. -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TThv49Gqa}{\ExternalLink}} \footnotesize Example of a non-overlapping and permitted overlapping case of \cpp{std::copy_backward}. \tcblower \cppfile{code_examples/algorithms/copy_backward_code.h} -\end{box-nobreak} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::copy_n}}{\texttt{std::copy\_n}}} \index{\cpp{std::copy_n}} @@ -66,11 +66,11 @@ \subsection{\texorpdfstring{\cpp{std::copy_n}}{\texttt{std::copy\_n}}} The algorithm cannot check whether the requested count is valid and does not go out of bounds, so this burden is on the caller. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/KGYab3WEr}{\ExternalLink}} \footnotesize Example of using \cpp{std::copy_n}. \tcblower \cppfile{code_examples/algorithms/copy_n_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::copy_if}, \cpp{std::remove_copy}, \cpp{std::remove_copy_if}}{\texttt{std::copy\_if}, \texttt{std::remove\_copy}, \texttt{std::remove\_copy\_if}}} \index{\cpp{std::copy_if}} @@ -87,11 +87,11 @@ \subsection{\texorpdfstring{\cpp{std::copy_if}, \cpp{std::remove_copy}, \cpp{std The \cpp{std::remove_copy} algorithm will copy elements that do not match the provided value. The \cpp{std::copy_if} and \cpp{std::remove_copy_if} algorithms will copy elements based on a predicate, with \cpp{std::copy_if} copying elements for which the predicate returns true and \cpp{std::remove_copy_if} copying elements for which the predicate returns false. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/vY6bTcqsr}{\ExternalLink}} \footnotesize Example demonstrating differences between \cpp{std::copy_if}, \cpp{std::remove_copy} and \cpp{std::remove_copy_if}. \tcblower \cppfile{code_examples/algorithms/copy_if_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::sample}}{\texttt{std::sample}}} \index{\cpp{std::sample}} @@ -103,11 +103,11 @@ \subsection{\texorpdfstring{\cpp{std::sample}}{\texttt{std::sample}}} The two domains of this algorithm are due to the stable nature of the sampling, maintaining the order of elements from the source range. This feature requires either the input range to be at least a forward range or the destination range needs to be a random-access range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/75WPEfjM9}{\ExternalLink}} \footnotesize Example of using \cpp{std::sample}. \tcblower \cppfile{code_examples/algorithms/sample_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::replace_copy}, \cpp{std::replace_copy_if}}{\texttt{std::replace\_copy}, \texttt{std::replace\_copy\_if}}} \index{\cpp{std::replace_copy}} @@ -121,11 +121,11 @@ \subsection{\texorpdfstring{\cpp{std::replace_copy}, \cpp{std::replace_copy_if}} \cppversions{\texttt{replace\_copy\_if}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TPTq4q8rr}{\ExternalLink}} \footnotesize Example of using \cpp{std::replace_copy} and \cpp{std::replace_copy_if}. \tcblower \cppfile{code_examples/algorithms/replace_copy_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::reverse_copy}}{\texttt{std::reverse\_copy}}} \index{\cpp{std::reverse_copy}} @@ -137,11 +137,11 @@ \subsection{\texorpdfstring{\cpp{std::reverse_copy}}{\texttt{std::reverse\_copy} Not to be confused with the \cpp{std::copy_backwards}, which copies elements in the original order. The \cpp{std::reverse_copy} does not permit the source, and the destination ranges to overlap. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1YM9WevcT}{\ExternalLink}} \footnotesize Example of using \cpp{std::reverse_copy}. \tcblower \cppfile{code_examples/algorithms/reverse_copy_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::rotate_copy}}{\texttt{std::rotate\_copy}}} \index{\cpp{std::rotate_copy}} @@ -153,8 +153,8 @@ \subsection{\texorpdfstring{\cpp{std::rotate_copy}}{\texttt{std::rotate\_copy}}} The input and output ranges cannot overlap. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/MWf5q3r9d}{\ExternalLink}} \footnotesize Example of using \cpp{std::rotate_copy}. \tcblower \cppfile{code_examples/algorithms/rotate_copy_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_14_uninitialized.tex b/chapters/03_algorithms_14_uninitialized.tex index 3869cb4..40c42de 100644 --- a/chapters/03_algorithms_14_uninitialized.tex +++ b/chapters/03_algorithms_14_uninitialized.tex @@ -10,16 +10,16 @@ \subsection{\texorpdfstring{\cpp{std::construct_at}, \cpp{std::destroy_at}}{\tex \index{\cpp{std::construct_at}} \index{\cpp{std::destroy_at}} -The \cpp{std::construct_at} and \cpp{std::destroy_at} algorithms will construct/destroy a single element at a given address. If additional arguments are specified, \cpp{std::construct_at} will forward these to the objects’ constructor. +The \cpp{std::construct_at} and \cpp{std::destroy_at} algorithms will construct/destroy a single element at a given address. If additional arguments are specified, \newline\cpp{std::construct_at} will forward these to the objects’ constructor. \cppversions{\texttt{construct\_at}}{\CC20}{\CC20}{N/A}{\CC20} \cppversions{\texttt{destroy\_at}}{\CC17}{\CC20}{N/A}{\CC20} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/fKMdoYd5r}{\ExternalLink}} \footnotesize Example of using \cpp{std::create_at} to create a \cpp{std::string} object using the arguments eight and ‘X’, which results in a string filled with eight copies of the X character. \tcblower \cppfile{code_examples/algorithms/create_at_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\cpp{std::uninitialized_value_construct},\newline\cpp{std::uninitialized_fill}, \newline\cpp{std::destroy}}{\texttt{std::uninitialized\_default\_construct},\newline\texttt{std::uninitialized\_value\_construct},\newline\texttt{std::uninitialized\_fill}, \newline\texttt{std::destroy}}} \index{\cpp{std::uninitialized_default_construct}} @@ -36,12 +36,11 @@ \subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\ \constraints{\texttt{forward\_range\newline forward\_iterator (counted)}}{}{}{} % Cleanup the counted variant situation -\begin{box-note} -\footnotesize -Example demonstrating the use of the counted variants of the uninitialized construction algorithm and the \cpp{std::destroy_n} algorithms. Note that for \cpp{std::string}, there is no difference between default and value construction. +\begin{codebox}[]{\href{https://compiler-explorer.com/z/snnhY37xM}{\ExternalLink}} +\footnotesize Example demonstrating the use of the counted variants of the uninitialized construction algorithm and the \cpp{std::destroy_n} algorithms. Note that for \cpp{std::string}, there is no difference between default and value construction. \tcblower \cppfile{code_examples/algorithms/uninitialized_constr_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::uninitialized_copy}, \cpp{std::uninitalized_move}}{\texttt{std::uninitialized\_copy}, \texttt{std::uninitalized\_move}}} \index{\cpp{std::uninitialized_copy}} @@ -54,18 +53,18 @@ \subsection{\texorpdfstring{\cpp{std::uninitialized_copy}, \cpp{std::uninitalize \constraints{\texttt{input\_range -> forward\_iterator\newline input\_iterator -> forward\_iterator (counted)}}{\texttt{forward\_range -> forward\_iterator\newline forward\_iterator -> forward\_iterator (counted)}}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/a9v4Tn46z}{\ExternalLink}} \footnotesize Example of using \cpp{std::uninitialized_copy} and \cpp{std::uninitialized_move}. \tcblower \cppfile{code_examples/algorithms/uninitialized_copy_code.h} -\end{box-note} +\end{codebox} \subsubsection{Transactional behaviour} The main benefit of using the uninitialized memory algorithms is that they correctly handle transactional behaviour. Transactionality is important in cases where the constructor of an object can throw. If one of the objects fails to construct, the algorithms will correctly roll back by destructing already constructed objects. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/6bP3P4ecK}{\ExternalLink}} \footnotesize Example demonstrating the roll-back behaviour of uninitialized algorithms when the third invocation of the constructor throws. Note that the exception is re-thrown after the partial work is rolled back. \tcblower \cppfile{code_examples/algorithms/transactional_code.h} -\end{box-note} +\end{codebox} diff --git a/chapters/03_algorithms_15_heap.tex b/chapters/03_algorithms_15_heap.tex index d7dd349..89526b4 100644 --- a/chapters/03_algorithms_15_heap.tex +++ b/chapters/03_algorithms_15_heap.tex @@ -20,21 +20,21 @@ \subsection{\texorpdfstring{\cpp{std::make_heap}, \cpp{std::push_heap}, \cpp{std The \cpp{std::make_heap} algorithm reorders elements in the given range such that the elements maintain the max-heap property, that is, the element at index $i$ compares greater or equal to the elements at indexes $2i+1$ and $2i+2$. -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/aYeTrofdK}{\ExternalLink}} \footnotesize Example of using \cpp{std::make_heap} to construct a max-heap and a min-heap (using a custom comparator). \tcblower \cppfile{code_examples/algorithms/make_heap_code.h} -\end{box-nobreak} +\end{codebox} The \cpp{std::push_heap} and \cpp{std::pop_heap} algorithms simulate push and pop operations for the max-heap data structure. However, because they operate on top of a range, they cannot manipulate the underlying data structure. Therefore, they use the last element of the range as the input/output. The \cpp{std::push_heap} algorithm will insert the last element of the range into the heap, and \cpp{std::pop_heap} will extract the maximum element to the last position of the range. As previously mentioned, both operations have logarithmic complexity. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/h34Eh5T7a}{\ExternalLink}} \footnotesize Example of using \cpp{std::push_heap} and \cpp{std::pop_heap}. \tcblower \cppfile{code_examples/algorithms/push_heap_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::sort_heap}}{\texttt{std::sort\_heap}}} \index{\cpp{std::sort_heap}} @@ -44,11 +44,11 @@ \subsection{\texorpdfstring{\cpp{std::sort_heap}}{\texttt{std::sort\_heap}}} \cppversions{\texttt{sort\_heap}}{\CC98}{\CC20}{N/A}{\CC20} \constraints{\texttt{random\_access\_range}}{}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/odrjGK7xb}{\ExternalLink}} \footnotesize Example of using \cpp{std::sort_heap}. \tcblower \cppfile{code_examples/algorithms/sort_heap_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_heap}, \cpp{std::is_heap_until}}{\texttt{std::is\_heap}, \texttt{std::is\_heap\_until}}} \index{\cpp{std::is_heap}} @@ -63,31 +63,31 @@ \subsection{\texorpdfstring{\cpp{std::is_heap}, \cpp{std::is_heap_until}}{\textt The two algorithms follow the same logic as \cpp{std::is_sorted} and \texttt{std::is\-\_sorted\-\_until}, returning a boolean and an iterator to the first out-of-order element respectively. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/oz41sxfWb}{\ExternalLink}} \footnotesize Example of using \cpp{std::is_heap} and \cpp{std::is_heap_until}. \tcblower \cppfile{code_examples/algorithms/is_heap_code.h} -\end{box-note} +\end{codebox} \subsection{Comparison with \texorpdfstring{\cpp{std::priority_queue}}{\texttt{std::priority\_queue}}} \index{\cpp{std::priority_queue}} As mentioned at the beginning of this section, the heap algorithms provide effectively the same functionality as \cpp{std::priority_queue}. To demonstrate the differences, let's look at two versions of a topk algorithm: an algorithm that takes a range and returns the top k elements in sorted order as a \cpp{std::vector}. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TvPv3sWsj}{\ExternalLink}} \footnotesize Example of implementing a topk algorithm using a \cpp{std::priority_queue}. \tcblower \cppfile{code_examples/extras/priority_queue_queue_code.h} -\end{box-note} +\end{codebox} The example relies on \CC20 concepts to provide a generic interface and should therefore work with any range on input, returning a \cpp{std::vector} of the same element type. When using the priority queue, we can utilize the simple \cpp{push()} and \cpp{pop()} interface provided (lines 12 and 14). However, extracting all data from the queue is only possible by repeatedly applying \cpp{pop()} until the queue is empty (line 20). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/EYhGcr8qj}{\ExternalLink}} \footnotesize Example of implementing a topk algorithm using heap algorithms. \tcblower \cppfile{code_examples/extras/priority_queue_heap_code.h} -\end{box-note} +\end{codebox} When using the heap algorithms, we need to manually manage the underlying data structure (lines 9-10 and 13–14). However, we do not need to extract the data, and on top of that, we could omit the final \cpp{std::sort_heap} (line 20) if we do not need the top k elements in sorted order. \ No newline at end of file diff --git a/chapters/03_algorithms_16_searching.tex b/chapters/03_algorithms_16_searching.tex index c66e014..53bba73 100644 --- a/chapters/03_algorithms_16_searching.tex +++ b/chapters/03_algorithms_16_searching.tex @@ -1,7 +1,7 @@ \section{Search and compare algorithms} The search and compare category provides straightforward linear (when compared against a single value) and quadratic (when compared against a range) complexity algorithms. - +\raggedbottom \subsection{\texorpdfstring{\cpp{std::find}, \cpp{std::find_if}, \cpp{std::find_if_not}}{\texttt{std::find}, \texttt{std::find\_if}, \texttt{std::find\_if\_not}}} \index{\cpp{std::find}} \index{\cpp{std::find_if}} @@ -14,21 +14,19 @@ \subsection{\texorpdfstring{\cpp{std::find}, \cpp{std::find_if}, \cpp{std::find_ \constraints{\texttt{input\_range}}{\texttt{forward\_range}}{\texttt{operator==} (\texttt{find})}{\texttt{unary\_predicate}} -A typical example of searching by value is finding delimiters: - -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/P993bPz39}{\ExternalLink}} \footnotesize Example of utilizing \cpp{std::find} to find delimiters in a string. \tcblower \cppfile{code_examples/algorithms/find_code.h} -\end{box-note} +\end{codebox} If we want to search for categories of elements, we can use \cpp{std::find_if} and \cpp{std::find_if_not} since these two variants search using a predicate. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/n9vTGsE4T}{\ExternalLink}} \footnotesize Example of utilizing \cpp{std::find_if_not} to find leading and trailing whitespace. \tcblower \cppfile{code_examples/algorithms/find_if_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::adjacent_find}}{\texttt{std::adjacent\_find}}} \index{\cpp{std::adjacent_find}} @@ -40,11 +38,11 @@ \subsection{\texorpdfstring{\cpp{std::adjacent_find}}{\texttt{std::adjacent\_fin If the algorithm finds a pair of elements, it will return an iterator to the first of the two elements (end iterator otherwise). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Paao79a68}{\ExternalLink}} \footnotesize Example of using \cpp{std::adjacent_find} to find the first pair of equal elements and the first pair of elements that sum up to more than ten. \tcblower \cppfile{code_examples/algorithms/adjacent_find_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::search_n}}{\texttt{std::search\_n}}} \index{\cpp{std::search_n}} @@ -56,11 +54,11 @@ \subsection{\texorpdfstring{\cpp{std::search_n}}{\texttt{std::search\_n}}} The interface to \cpp{std::search_n} can be a bit confusing. The algorithm accepts the number of instances and the value to search for as two consecutive arguments, followed by an optional custom comparator function. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/q737jor9n}{\ExternalLink}} \footnotesize Example of using \cpp{std::search_n} to find two consecutive elements equal to $3$, three elements equal to $3$ (in modulo $5$ arithmetic) and finally, two elements equal to $0$. \tcblower \cppfile{code_examples/algorithms/search_n_code.h} -\end{box-note} +\end{codebox} Note that \cpp{std::search_n} is one exception to the \cpp{_n} naming scheme. @@ -74,11 +72,11 @@ \subsection{\texorpdfstring{\cpp{std::find_first_of}}{\texttt{std::find\_first\_ Note that we are shifting from linear search to $O(m*n)$ time complexity since, for each element of the first range, we need to compare it to all elements in the second range (worst case). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/YnTGErxTo}{\ExternalLink}} \footnotesize Example of using \cpp{std::find_first_of}. \tcblower \cppfile{code_examples/algorithms/find_first_of_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::search}, \cpp{std::find_end}}{\texttt{std::search}, \texttt{std::find\_end}}} \index{\cpp{std::search}} @@ -90,24 +88,24 @@ \subsection{\texorpdfstring{\cpp{std::search}, \cpp{std::find_end}}{\texttt{std: \cppversions{\texttt{search, find\_end}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_range)}}{\texttt{(forward\_range, forward\_range)}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Gq94oEo9K}{\ExternalLink}} \footnotesize Example of using \cpp{std::search} and \cpp{std::find_end}. \tcblower \cppfile{code_examples/algorithms/search_code.h} -\end{box-note} +\end{codebox} \subsubsection{Searchers} Since \CC17, we also can specify custom searchers for the search algorithm. Apart from the basic one, the standard implements Boyer-Moore and Boyer-Moore-Horspool string searchers that offer different best-case, worst-case and average complexity. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7MKfzoP77}{\ExternalLink}} \footnotesize Example of using \cpp{std::search} with custom searchers. \tcblower \cppfile{code_examples/algorithms/searchers_code.h} \index{\cpp{std::default_searcher}} \index{\cpp{std::boyer_moore_searcher}} \index{\cpp{std::boyer_moore_horspool_searcher}} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::count}, \cpp{std::count_if}}{\texttt{std::count}, \texttt{std::count\_if}}} \index{\cpp{std::count}} @@ -120,11 +118,11 @@ \subsection{\texorpdfstring{\cpp{std::count}, \cpp{std::count_if}}{\texttt{std:: The element searched for can be specified using a value (\cpp{std::count}) or a predicate (\cpp{std::count_if}). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/sP8eqd45j}{\ExternalLink}} \footnotesize Example of using \cpp{std::count} and \cpp{std::count_if}. \tcblower \cppfile{code_examples/algorithms/count_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::equal}, \cpp{std::mismatch}}{\texttt{std::equal}, \texttt{std::mismatch}}} \index{\cpp{std::equal}} @@ -135,29 +133,29 @@ \subsection{\texorpdfstring{\cpp{std::equal}, \cpp{std::mismatch}}{\texttt{std:: \cppversions{\texttt{equal}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/16jEbK3qo}{\ExternalLink}} \footnotesize Example of using \cpp{std::equal}. \tcblower \cppfile{code_examples/algorithms/equal_code.h} -\end{box-note} +\end{codebox} The \cpp{std::mismatch} algorithm behaves exactly like \cpp{std::equal}; however, instead of returning a simple boolean, it returns a pair of iterators denoting the mismatched elements. \cppversions{\texttt{mismatch}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/cY4Eo3MYv}{\ExternalLink}} \footnotesize Example of using \cpp{std::mismatch}. \tcblower \cppfile{code_examples/algorithms/mismatch_code.h} -\end{box-note} +\end{codebox} On top of the basic variants, both \cpp{std::equal} and \cpp{std::mismatch} offer a version where both ranges are fully specified (since C++14). These versions can detect mismatches beyond the first range's scope. \constraints{\texttt{(input\_range, input\_range)} since \CC14}{(forward\_range, forward\_range)}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/f93PY3fPM}{\ExternalLink}} \footnotesize Example of detecting a mismatch beyond the scope of the first range. \tcblower \cppfile{code_examples/algorithms/equal_with_range_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_17_minmax.tex b/chapters/03_algorithms_17_minmax.tex index 3f5952b..5dff7fb 100644 --- a/chapters/03_algorithms_17_minmax.tex +++ b/chapters/03_algorithms_17_minmax.tex @@ -4,21 +4,21 @@ \section{Min-Max algorithms} For functions that accept an \cpp{std::initializer_list}, it is worth keeping in mind that \cpp{std::initializer_list} is constructed by copy; its internal array of elements is copy constructed from the listed elements. Therefore we need to be careful when using \cpp{std::initializer_list} outside of compile-time contexts. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/14enPTMbj}{\ExternalLink}} \footnotesize Example demonstrating case when utilizing \cpp{std::initializer_list} leads to excessive copies. \tcblower \cppfile{code_examples/theory/initializer_list_code.h} -\end{box-note} +\end{codebox} In this example, using \cpp{std::initializer_list} leads to six copies (which we count using the static data member \cpp{X::copy_cnt}). Five copies result from passing in the variables \texttt{a} to \texttt{e} into the \cpp{std::initializer_list}, and one is the result of the return from \cpp{std::max}. In rare cases, we can force constness on a mutable entity. If the constness is undesirable, using \cpp{const_cast} to cast away the const is an option. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/64qvaf3cj}{\ExternalLink}} \footnotesize Example demonstrating the valid and invalid uses for \cpp{const_cast}. \tcblower \cppfile{code_examples/theory/const_cast_code.h} -\end{box-note} +\end{codebox} Please remember that when using casts like \cpp{const_cast}, you effectively override the compiler's judgment. Therefore it is entirely up to you to ensure that the given cast is valid. @@ -32,29 +32,29 @@ \subsection{\texorpdfstring{\cpp{std::min}, \cpp{std::max}, \cpp{std::minmax}}{\ \cppversions{\texttt{min, max}}{\CC98}{\CC14}{N/A}{\CC20} \cppversions{\texttt{minmax}}{\CC11}{\CC14}{N/A}{\CC20} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/5GKe13fd5}{\ExternalLink}} \footnotesize Example demonstrating use of \cpp{std::min} and \cpp{std::max}. \tcblower \cppfile{code_examples/algorithms/min_max_code.h} -\end{box-note} +\end{codebox} Capturing the result by value gets a bit more complicated with \cpp{std::minmax}, which returns a \cpp{std::pair} of const references. To avoid dangling references to expired prvalues we must explicitly name the result type. Unfortunately, there is no way to work around this problem when using \cpp{auto} or structured binding. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/WGahheGfT}{\ExternalLink}} \footnotesize Example demonstrating use of \cpp{std::minmax}. \tcblower \cppfile{code_examples/algorithms/minmax_code.h} -\end{box-note} +\end{codebox} The C++14 and C++20 standards introduced additional variants of the min-max algorithms that return by value. The change to return by value resolves the dangling reference issue while simultaneously introducing the potential for excessive copies. \constraints{\CC14: \texttt{initializer\_list}\newline \CC20 range version: \texttt{input\_range}}{}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/P4r5ze416}{\ExternalLink}} \footnotesize Example of \cpp{std::initializer_list} and range variants of \cpp{std::min}, \cpp{std::max} and \cpp{std::minmax}. \tcblower \cppfile{code_examples/algorithms/minmax_extra_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::clamp}}{\texttt{std::clamp}}} \index{\cpp{std::clamp}} @@ -71,11 +71,11 @@ \subsection{\texorpdfstring{\cpp{std::clamp}}{\texttt{std::clamp}}} Because the algorithm accepts its arguments by const reference and returns a const reference, it shares the same issues as the min-max algorithms regarding const correctness and dangling references. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/3fcTvTEfM}{\ExternalLink}} \footnotesize Examples of using \cpp{std::clamp} with prvalues and with lvalues and \cpp{const_cast} to get mutable access to the original variable. \tcblower \cppfile{code_examples/algorithms/clamp_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \newline\cpp{std::minmax_element}}{\texttt{std::min\_element}, \texttt{std::max\_element}, \textCR\texttt{std::minmax\_element}}} @@ -91,18 +91,18 @@ \subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \new \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/M1v9Yb93K}{\ExternalLink}} \footnotesize Example of using element versions of min-max algorithms. \tcblower \cppfile{code_examples/algorithms/min_element_code.h} -\end{box-note} +\end{codebox} You might be wondering whether we can cause the same dangling reference (iterator) issue with the element versions of min-max algorithms. Fortunately, one very nice feature of C++20 ranges is the protection against precisely this problem. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vq7d3bq1K}{\ExternalLink}} \footnotesize Example demonstrating protection from dangling iterators. \tcblower \cppfile{code_examples/algorithms/min_element_dangling_code.h} -\end{box-note} +\end{codebox} All ranged versions of algorithms that return iterators will return the \texttt{std::ranges\-::dangling} type when invoked on a temporary range. This would preclude the use case of using \cpp{std::span} to sub-reference a range, which is why the range algorithms have an additional concept of a \cpp{borrowed_range}. Such ranges can be passed in as temporaries since they do not own their elements. \ No newline at end of file diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex deleted file mode 100644 index 25ea711..0000000 --- a/chapters/04_ranges_in_depth.tex +++ /dev/null @@ -1,284 +0,0 @@ -\chapter{Introduction to Ranges} - -The \CC20 standard introduced the Ranges library (a.k.a. STLv2), which effectively replaces existing algorithms and facilities. -In this chapter, we will go over the main changes. - -Note that Ranges are one of the features that landed in \CC20 in a partial state. -Despite \CC23 introducing plenty of additional features, we already know that many features will still not make it into \CC23. - -\section{Reliance on concepts} - -The standard has always described what is required of each argument passed to an algorithm. However, this description was purely informative, and the language did not offer first-class tools to enforce these requirements. Because of this, the error messages were often confusing. - -Concepts are a new C++20 language feature that permits library implementors to constrain the arguments of generic code. Concepts are beyond the scope of this book; however, there are two consequences worth noting. - -First, the definitions of all concepts are now part of the standard library, e.g. you can look up what exactly it means for a type to be a \cpp{random_access_range} and also use these concepts to constrain your code. - -\begin{box-nobreak} -\footnotesize Example of using standard concepts in user code. The function accepts any random access range as the first argument and an output iterator with the same underlying type as the second argument. -\tcblower -\cppfile{code_examples/ranges/concepts_code.h} -\end{box-nobreak} - -Second, error messages now reference unsatisfied constraints instead of reporting an error deep in the library implementation. - -\begin{minted}[fontsize=\footnotesize]{cpp} -note: candidate: 'template - requires (random_access_iterator<_Iter>) -\end{minted} - -\section{Notion of a Range} - -Non-range algorithms have always operated strictly using iterators. Conceptually, the range of elements has been defined by two iterators \cpp{[first, last)} or when working with all elements from a standard data structure \cpp{[begin, end)}. - -Range algorithms formally define a range as an iterator and a sentinel pair. Moving the sentinel to a different type unlocks the potential to simplify code where an end iterator doesn't naturally map to an element. The standard offers two default sentinel types, \cpp{std::default_sentinel} and \cpp{std::unreachable_sentinel}. - -Apart from simplifying certain use cases, sentinels also allow for infinite ranges and potential performance improvements. - -\begin{box-note} -\footnotesize Example of an infinite range when the data guarantees termination. Using \cpp{std::unreachable_sentinel} causes the boundary check to be optimized-out, removing one comparison from the loop. -\tcblower -\cppfile{code_examples/ranges/infinite_code.h} -\end{box-note} - -We can only use this approach when we have a contextual guarantee that the algorithm will terminate without going out of bounds. However, this removes one of the few instances when algorithms couldn't perform as well as handwritten code. - -And finally, with the introduction of the range, algorithms now provide range overloads, leading to more concise and easier-to-read code. - -\begin{box-note} -\footnotesize Example of using the range overload of \cpp{std::sort}. -\tcblower -\cppfile{code_examples/ranges/rangified_code.h} -\end{box-note} - -\section{Projections} - -Each range algorithm comes with an additional argument, the projection. -The projection is effectively a baked-in transform operation applied before an element is passed into the main functor. - -The projection can be any invocable, which includes member pointers. - -\begin{box-note} -\footnotesize Example of using a projection to sort elements of a range based on a computed value from an element method. -\tcblower -\cppfile{code_examples/ranges/projection_code.h} -\end{box-note} - -Because the projection result is only used for the main functor, it does not modify the output of the algorithm except for \cpp{std::ranges::transform}. - -\begin{box-note} -\footnotesize Example of the difference between \cpp{std::ranges::copy_if}, which uses the projection result for the predicate and \cpp{std::ranges::transform}, which uses the projection result as the input for the transformation function (therefore making it affect the output type). -\tcblower -\cppfile{code_examples/ranges/projected_type_code.h} -\end{box-note} - -\section{Dangling iterator protection} - -Because range versions of algorithms provide overloads that accept entire ranges, they open the potential for dangling iterators when users pass in a temporary range. - -However, for some ranges, passing in a temporary is not an issue. To distinguish these two cases and to prevent dangling iterators, the range library has the concepts of a borrowed range and a special type \cpp{std::ranges::dangling}. - -Borrowed ranges are ranges that "borrow" their elements from another range. Primary examples are \cpp{std::string_view} and \cpp{std::span}. For borrowed ranges, the lifetime of the elements is not tied to the lifetime of the range itself. - -\begin{box-note} -\footnotesize Example demonstrating the different handling for a borrowed range \cpp{std::string_view} and a \cpp{std::string}. -\tcblower -\cppfile{code_examples/ranges/dangling_code.h} -\end{box-note} - -User types can declare themselves as borrowed ranges by specializing the \texttt{enable\-\_borrowed\-\_range} constant. - -\begin{box-note} -\footnotesize Example demonstrating declaring MyView as a borrowed range. -\tcblower -\cppfile{code_examples/ranges/borrowed_optin_code.h} -\end{box-note} - -\section{Views} - -One of the core problems with algorithms is that they are not easily composable. As a result, the code using algorithms is often quite verbose and requires additional copies when working with immutable data. - -Views aim to address this problem by providing cheap to copy and move facilities composed at compile-time. - -\begin{box-note} -\footnotesize Example of composing several views. -\tcblower -\cppfile{code_examples/ranges/view_demo_code.h} -\end{box-note} - -In this chapter, we will go over all the views the standard library offers. - -\subsection{\texorpdfstring{\cpp{std::views::keys}, \cpp{std::views::values}}{\texttt{std::views::keys}, \texttt{std::views::values}}} -\index{\cpp{std::views::keys}} -\index{\cpp{std::views::values}} - -The \cpp{std::views::keys} and \cpp{std::views::values} operate on ranges of pair-like elements. -The \cpp{std::views::keys} will produce a range of the first elements from each pair. -The \cpp{std::views::values} will produce a range of the second elements from each pair. - -\begin{box-note} -\footnotesize Example of decomposing a \cpp{std::unordered_map} into a view over the keys and a view over the values. -\tcblower -\cppfile{code_examples/views/keys_values_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elements}}} -\index{\cpp{std::views::elements}} - -The \cpp{std::views::elements} will produce a range of the Nth elements from a range of tuple-like elements. - -\begin{box-note} -\footnotesize Example of creating element views for each tuple's second and third elements in the source range. -\tcblower -\cppfile{code_examples/views/elements_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::transform}}{\texttt{std::views::transform}}} -\index{\cpp{std::views::transform}} - -The transform view applies a transformation functor to every element of the range. - -\begin{box-note} -\footnotesize Example of using \cpp{std::views::transform} that also changes the base type of the range. -\tcblower -\cppfile{code_examples/views/transform_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::take}, \cpp{std::views::take_while}}{\texttt{std::views::take}, \texttt{std::views::take\_while}}} -\index{\cpp{std::views::take}} -\index{\cpp{std::views::take_while}} - -Both views are formed from the leading elements of the source range. In the case of \cpp{std::views::take}, the view consists of the first N elements. In the case of \cpp{std::views::take_while}, the view consists of the sequence of elements for which the predicate evaluates true. - -\begin{box-note} -\footnotesize Example of a view of the first three elements and a view of the leading sequence of odd elements. -\tcblower -\cppfile{code_examples/views/take_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::drop}, \cpp{std::views::drop_while}}{\texttt{std::views::drop}, \texttt{std::views::drop\_while}}} -\index{\cpp{std::views::drop}} -\index{\cpp{std::views::drop_while}} - -The drop views are the inverse of take views. -The \cpp{std::views::drop} consists of all but the first N elements. -The \cpp{std::views::drop_while} consists of all but the leading sequence of elements for which the predicate evaluates true. - -\begin{box-note} -\footnotesize Example of a view of all but the first three elements and a view skipping over the leading sequence of odd elements. -\tcblower -\cppfile{code_examples/views/drop_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::filter}}{\texttt{std::views::filter}}} -\index{\cpp{std::views::filter}} - -The filter view consists of all elements that satisfy the provided predicate. - -\begin{box-note} -\footnotesize Example of a view of even elements. -\tcblower -\cppfile{code_examples/views/filter_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::reverse}}} -\index{\cpp{std::views::reverse}} - -The reverse view is the reverse iteration view for bidirectional ranges. - -\begin{box-note} -\footnotesize Example of a reverse view. -\tcblower -\cppfile{code_examples/views/reverse_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::counted}}} -\index{\cpp{std::views::counted}} - -The counted view adapts an iterator and number of elements into a view. - -\begin{box-note} -\footnotesize Example of using a counted view to iterate over a subrange. -\tcblower -\cppfile{code_examples/views/counted_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::common}}{\texttt{std::views::common}}} -\index{\cpp{std::views::common}} - -The common view adapts a view into a common range, a range with a begin and end iterator of matching types. Non-range versions of algorithms require a common range. - -\begin{box-note} -\footnotesize Example of using adapting a view for a non-range algorithm. -\tcblower -\cppfile{code_examples/views/common_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} -\index{\cpp{std::views::all}} - -The all view is a view of all the elements from a range. - -\begin{box-note} -\footnotesize Example of creating a view over all elements. Note that view over all elements is the default. -\tcblower -\cppfile{code_examples/views/all_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \newline\cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}, \textCR\texttt{std::views::join\_view}}} -\index{\cpp{std::views::split}} -\index{\cpp{std::views::lazy_split}} -\index{\cpp{std::views::join_view}} - -The two split views split a single range into a view over sub-ranges. However, they differ in their implementation. -The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \texttt{std::view::\-lazy\_split} does not, but it does support input ranges. - -\begin{box-note} -\footnotesize Example of using split view to parse a version number. -\tcblower -\cppfile{code_examples/views/split_code.h} -\end{box-note} - -The join view flattens a view of ranges. - -\begin{box-note} -\footnotesize Example of using \cpp{std::views::lazy_split} to split a string into tokens and then join them using the \cpp{std::views::join}. -\tcblower -\cppfile{code_examples/views/join_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::empty}, \cpp{std::views::single}}{\texttt{std::views::empty}, \texttt{std::views::single}}} -\index{\cpp{std::views::empty}} -\index{\cpp{std::views::single}} - -The empty view is an empty view (containing no elements), and the single view is a view that contains a single element. -Note that the view owns the single element, which will be copied/moved alongside the view. - -\begin{box-note} -\footnotesize Example of using \cpp{std::views::empty} and \cpp{std::views::single}. -\tcblower -\cppfile{code_examples/views/single_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::iota}}{\texttt{std::views::iota}}} -\index{\cpp{std::views::iota}} - -The iota view represents a generated sequence formed by repeatedly incrementing an initial value. - -\begin{box-note} -\footnotesize Example of using the finite and infinite iota view. -\tcblower -\cppfile{code_examples/views/iota_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::istream}}{\texttt{std::views::istream}}} -\index{\cpp{std::views::istream}} - -The istream view provides similar functionality to istream iterator, except in the form of a view. -It represents a view obtained by successively applying the istream input operator. - -\begin{box-note} -\footnotesize Example of using istream view. -\tcblower -\cppfile{code_examples/views/istream_code.h} -\end{box-note} \ No newline at end of file diff --git a/chapters/04_views_00_main.tex b/chapters/04_views_00_main.tex new file mode 100644 index 0000000..f9281d4 --- /dev/null +++ b/chapters/04_views_00_main.tex @@ -0,0 +1,2 @@ +\input{chapters/04_views_01_intro_ranges} +\input{chapters/04_views_02_the_views} \ No newline at end of file diff --git a/chapters/04_views_01_intro_ranges.tex b/chapters/04_views_01_intro_ranges.tex new file mode 100644 index 0000000..d891e71 --- /dev/null +++ b/chapters/04_views_01_intro_ranges.tex @@ -0,0 +1,116 @@ +\chapter{Introduction to Ranges} + +The \CC20 standard introduced the Ranges library (a.k.a. STLv2), which effectively replaces existing algorithms and facilities. +In this chapter, we will go over the main changes. + +Note that Ranges are one of the features that landed in \CC20 in a partial state. +Despite \CC23 introducing plenty of additional features, we already know that many features will still not make it into \CC23. + +\section{Reliance on concepts} + +The standard has always described what is required of each argument passed to an algorithm. However, this description was purely informative, and the language did not offer first-class tools to enforce these requirements. Because of this, the error messages were often confusing. + +Concepts are a new C++20 language feature that permits library implementors to constrain the arguments of generic code. Concepts are beyond the scope of this book; however, there are two consequences worth noting. + +First, the definitions of all concepts are now part of the standard library, e.g. you can look up what exactly it means for a type to be a \cpp{random_access_range} and also use these concepts to constrain your code. +\raggedbottom + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/ajd55nzsr}{\ExternalLink}} +\footnotesize Example of using standard concepts in user code. The function accepts any random access range as the first argument and an output iterator with the same underlying type as the second argument. +\tcblower +\cppfile{code_examples/ranges/concepts_code.h} +\end{codebox} + +Second, error messages now reference unsatisfied constraints instead of reporting an error deep in the library implementation. + +\begin{minted}[fontsize=\footnotesize]{cpp} +note: candidate: 'template + requires (random_access_iterator<_Iter>) +\end{minted} + +\section{Notion of a Range} + +Non-range algorithms have always operated strictly using iterators. Conceptually, the range of elements has been defined by two iterators \cpp{[first, last)} or when working with all elements from a standard data structure \cpp{[begin, end)}. + +Range algorithms formally define a range as an iterator and a sentinel pair. Moving the sentinel to a different type unlocks the potential to simplify code where an end iterator doesn't naturally map to an element. The standard offers two default sentinel types, \cpp{std::default_sentinel} and \cpp{std::unreachable_sentinel}. + +Apart from simplifying certain use cases, sentinels also allow for infinite ranges and potential performance improvements. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6o5M9fqE7}{\ExternalLink}} +\footnotesize Example of an infinite range when the data guarantees termination. Using \cpp{std::unreachable_sentinel} causes the boundary check to be optimized-out, removing one comparison from the loop. +\tcblower +\cppfile{code_examples/ranges/infinite_code.h} +\end{codebox} + +We can only use this approach when we have a contextual guarantee that the algorithm will terminate without going out of bounds. However, this removes one of the few instances when algorithms couldn't perform as well as handwritten code. + +And finally, with the introduction of the range, algorithms now provide range overloads, leading to more concise and easier-to-read code. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/47h5MMKKv}{\ExternalLink}} +\footnotesize Example of using the range overload of \cpp{std::sort}. +\tcblower +\cppfile{code_examples/ranges/rangified_code.h} +\end{codebox} + +\section{Projections} + +Each range algorithm comes with an additional argument, the projection. +The projection is effectively a baked-in transform operation applied before an element is passed into the main functor. + +The projection can be any invocable, which includes member pointers. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/4xvjn8jbc}{\ExternalLink}} +\footnotesize Example of using a projection to sort elements of a range based on a computed value from an element method. +\tcblower +\cppfile{code_examples/ranges/projection_code.h} +\end{codebox} + +Because the projection result is only used for the main functor, it does not modify the output of the algorithm except for \cpp{std::ranges::transform}. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/Tr3zY5YPd}{\ExternalLink}} +\footnotesize Example of the difference between \cpp{std::ranges::copy_if}, which uses the projection result for the predicate and \cpp{std::ranges::transform}, which uses the projection result as the input for the transformation function (therefore making it affect the output type). +\tcblower +\cppfile{code_examples/ranges/projected_type_code.h} +\end{codebox} + +\section{Dangling iterator protection} + +Because range versions of algorithms provide overloads that accept entire ranges, they open the potential for dangling iterators when users pass in a temporary range. + +However, for some ranges, passing in a temporary is not an issue. To distinguish these two cases and to prevent dangling iterators, the range library has the concepts of a borrowed range and a special type \cpp{std::ranges::dangling}. + +Borrowed ranges are ranges that "borrow" their elements from another range. Primary examples are \cpp{std::string_view} and \cpp{std::span}. For borrowed ranges, the lifetime of the elements is not tied to the lifetime of the range itself. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/oY8zYGs7r}{\ExternalLink}} +\footnotesize Example demonstrating the different handling for a borrowed range \cpp{std::string_view} and a \cpp{std::string}. +\tcblower +\cppfile{code_examples/ranges/dangling_code.h} +\end{codebox} + +User types can declare themselves as borrowed ranges by specializing the \texttt{enable\-\_borrowed\-\_range} constant. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Tnh687nxj}{\ExternalLink}} +\footnotesize Example demonstrating declaring MyView as a borrowed range. +\tcblower +\cppfile{code_examples/ranges/borrowed_optin_code.h} +\end{codebox} + +\section{Views} + +One of the core problems with algorithms is that they are not easily composable. As a result, the code using algorithms is often quite verbose and requires additional copies when working with immutable data. + +Views aim to address this problem by providing cheap to copy and move facilities composed at compile-time. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/aM3vxs1f8}{\ExternalLink}} +\footnotesize Example of composing several views. +\tcblower +\cppfile{code_examples/ranges/view_demo_code.h} +\end{codebox} + +It is worth noting that views are stateful and should be treated as stateful lambdas (e.g. for iterator validity and thread safety). + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/fK91qd3M4}{\ExternalLink}} +\footnotesize Example of \cpp{std::views::drop} caching behaviour. +\tcblower +\cppfile{code_examples/ranges/stateful_code.h} +\end{codebox} \ No newline at end of file diff --git a/chapters/04_views_02_the_views.tex b/chapters/04_views_02_the_views.tex new file mode 100644 index 0000000..e4f98e4 --- /dev/null +++ b/chapters/04_views_02_the_views.tex @@ -0,0 +1,178 @@ +\chapter{The views} + +In this chapter, we will go over all the views the standard library offers. + +\section{\texorpdfstring{\cpp{std::views::keys}, \cpp{std::views::values}}{\texttt{std::views::keys}, \texttt{std::views::values}}} +\index{\cpp{std::views::keys}} +\index{\cpp{std::views::values}} + +The \cpp{std::views::keys} and \cpp{std::views::values} operate on ranges of pair-like elements. +The \cpp{std::views::keys} will produce a range of the first elements from each pair. +The \cpp{std::views::values} will produce a range of the second elements from each pair. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vs1WjT1cM}{\ExternalLink}} +\footnotesize Example of decomposing a \cpp{std::unordered_map} into a view over the keys and a view over the values. +\tcblower +\cppfile{code_examples/views/keys_values_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elements}}} +\index{\cpp{std::views::elements}} + +The \cpp{std::views::elements} will produce a range of the Nth elements from a range of tuple-like elements. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/efjT5o8G8}{\ExternalLink}} +\footnotesize Example of creating element views for each tuple's second and third elements in the source range. +\tcblower +\cppfile{code_examples/views/elements_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::transform}}{\texttt{std::views::transform}}} +\index{\cpp{std::views::transform}} + +The transform view applies a transformation functor to every element of the range. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/qanMb3GqG}{\ExternalLink}} +\footnotesize Example of using \cpp{std::views::transform} that also changes the base type of the range. +\tcblower +\cppfile{code_examples/views/transform_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::take}, \cpp{std::views::take_while}}{\texttt{std::views::take}, \texttt{std::views::take\_while}}} +\index{\cpp{std::views::take}} +\index{\cpp{std::views::take_while}} + +Both views are formed from the leading elements of the source range. In the case of \cpp{std::views::take}, the view consists of the first N elements. In the case of \cpp{std::views::take_while}, the view consists of the sequence of elements for which the predicate evaluates true. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6vavoW6T3}{\ExternalLink}} +\footnotesize Example of a view of the first three elements and a view of the leading sequence of odd elements. +\tcblower +\cppfile{code_examples/views/take_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::drop}, \cpp{std::views::drop_while}}{\texttt{std::views::drop}, \texttt{std::views::drop\_while}}} +\index{\cpp{std::views::drop}} +\index{\cpp{std::views::drop_while}} + +The drop views are the inverse of take views. +The \cpp{std::views::drop} consists of all but the first N elements. +The \cpp{std::views::drop_while} consists of all but the leading sequence of elements for which the predicate evaluates true. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Yn9PKe1aY}{\ExternalLink}} +\footnotesize Example of a view of all but the first three elements and a view skipping over the leading sequence of odd elements. +\tcblower +\cppfile{code_examples/views/drop_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::filter}}{\texttt{std::views::filter}}} +\index{\cpp{std::views::filter}} + +The filter view consists of all elements that satisfy the provided predicate. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/4hYba6qf3}{\ExternalLink}} +\footnotesize Example of a view of even elements. +\tcblower +\cppfile{code_examples/views/filter_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::reverse}}} +\index{\cpp{std::views::reverse}} + +The reverse view is the reverse iteration view for bidirectional ranges. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/eY7ohjGTM}{\ExternalLink}} +\footnotesize Example of a reverse view. +\tcblower +\cppfile{code_examples/views/reverse_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::counted}}} +\index{\cpp{std::views::counted}} + +The counted view adapts an iterator and number of elements into a view. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/rcfe69aEo}{\ExternalLink}} +\footnotesize Example of using a counted view to iterate over a subrange. +\tcblower +\cppfile{code_examples/views/counted_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::common}}{\texttt{std::views::common}}} +\index{\cpp{std::views::common}} + +The common view adapts a view into a common range, a range with a begin and end iterator of matching types. Non-range versions of algorithms require a common range. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/WbGrGK56z}{\ExternalLink}} +\footnotesize Example of using adapting a view for a non-range algorithm. +\tcblower +\cppfile{code_examples/views/common_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} +\index{\cpp{std::views::all}} + +The all view is a view of all the elements from a range. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1hf9ajK81}{\ExternalLink}} +\footnotesize Example of creating a view over all elements. Note that view over all elements is the default. +\tcblower +\cppfile{code_examples/views/all_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \newline\cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}, \textCR\texttt{std::views::join\_view}}} +\index{\cpp{std::views::split}} +\index{\cpp{std::views::lazy_split}} +\index{\cpp{std::views::join_view}} + +The two split views split a single range into a view over sub-ranges. However, they differ in their implementation. +The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \texttt{std::view::\-lazy\_split} does not, but it does support input ranges. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/cjxdMjYhr}{\ExternalLink}} +\footnotesize Example of using split view to parse a version number. +\tcblower +\cppfile{code_examples/views/split_code.h} +\end{codebox} + +The join view flattens a view of ranges. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/MT7qbqjT6}{\ExternalLink}} +\footnotesize Example of using \cpp{std::views::lazy_split} to split a string into tokens and then join them using the \cpp{std::views::join}. +\tcblower +\cppfile{code_examples/views/join_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::empty}, \cpp{std::views::single}}{\texttt{std::views::empty}, \texttt{std::views::single}}} +\index{\cpp{std::views::empty}} +\index{\cpp{std::views::single}} + +The empty view is an empty view (containing no elements), and the single view is a view that contains a single element. +Note that the view owns the single element, which will be copied/moved alongside the view. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/d91M3o36e}{\ExternalLink}} +\footnotesize Example of using \cpp{std::views::empty} and \cpp{std::views::single}. +\tcblower +\cppfile{code_examples/views/single_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::iota}}{\texttt{std::views::iota}}} +\index{\cpp{std::views::iota}} + +The iota view represents a generated sequence formed by repeatedly incrementing an initial value. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/cEMdszTjE}{\ExternalLink}} +\footnotesize Example of using the finite and infinite iota view. +\tcblower +\cppfile{code_examples/views/iota_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::istream}}{\texttt{std::views::istream}}} +\index{\cpp{std::views::istream}} + +The istream view provides similar functionality to istream iterator, except in the form of a view. +It represents a view obtained by successively applying the istream input operator. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7r6x74PsY}{\ExternalLink}} +\footnotesize Example of using istream view. +\tcblower +\cppfile{code_examples/views/istream_code.h} +\end{codebox} \ No newline at end of file diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 76d2238..f1cffd8 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -7,27 +7,27 @@ \section{Argument-dependent lookup (ADL)} When calling a method without qualification (i.e. not specifying the namespace), the compiler needs to determine the set of candidate functions. As a first step, the compiler will do an unqualified name lookup, which starts at the local scope and examines the parent scopes until it finds the first instance of the name (at which point it stops). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/GbbM5x98E}{\ExternalLink}} \footnotesize Example of unqualified lookup. Both calls to \cpp{some_call} will resolve to \cpp{::A::B::some_call} since this is the first instance discovered by the compiler. \tcblower \cppfile{code_examples/theory/adl_unqalified_code.h} -\end{box-note} +\end{codebox} Due to the simplicity of unqualified lookup, we need an additional mechanism to discover overloads. Notably, it is a requirement for operator overloading since operator calls are unqualified. This is where argument-dependent lookup comes in. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/odqGe47jW}{\ExternalLink}} \footnotesize Without ADL, any call to a custom operator overload would have to be fully qualified, requiring the function call syntax. \tcblower \cppfile{code_examples/theory/adl_code.h} -\end{box-note} +\end{codebox} While the full rules for ADL are quite complex, the heavily simplified version is that the compiler will also consider the innermost namespace of all the arguments when determining the viable function overloads. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/ebvhrsqKW}{\ExternalLink}} \footnotesize \tcblower \cppfile{code_examples/theory/adl_simple_code.h} -\end{box-note} +\end{codebox} Arguably the true power of ADL lies in the interactions with other language features, so let's look at how ADL interacts with friend functions and function objects. @@ -35,11 +35,11 @@ \subsection{Friend functions vs ADL} Friend functions (when defined inline) do not participate in the normal lookup (they are part of the surrounding namespace but are not visible). However, they are still visible to ADL, which permits a common implementation pattern for a default implementation with a customization point through ADL. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/nYME37jqE}{\ExternalLink}} \footnotesize Example demonstrating a default implementation with a customization point through ADL. The default implementation needs to be discovered during the unqualified lookup; therefore, if any custom implementation is visible in the surrounding namespaces, it will block this discovery. \tcblower \cppfile{code_examples/theory/adl_default_code.h} -\end{box-note} +\end{codebox} \subsection{Function objects vs ADL} @@ -47,28 +47,184 @@ \subsection{Function objects vs ADL} Argument-dependent lookup will not consider non-function symbols. This means that a function object or a lambda must be either visible to an unqualified lookup or need to be fully qualified. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/v8KobovKf}{\ExternalLink}} \footnotesize Example demonstrating a lambda stored in an inline variable that can only be invoked through a qualified call. \tcblower \cppfile{code_examples/theory/adl_nonfunc_code.h} -\end{box-note} +\end{codebox} Finally, discovering a non-function symbol during the unqualified lookup phase will prevent ADL completely. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/f75bTc7aE}{\ExternalLink}} \footnotesize Example demonstrating a non-function object preventing ADL, making a friend function impossible to invoke. \tcblower \cppfile{code_examples/theory/adl_shutdown_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\CC20 ADL customization point}{C++20 ADL customization point}} With the introduction of concepts in \CC20, we now have a cleaner way to introduce a customization point using ADL. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Kr13she7v}{\ExternalLink}} \footnotesize The concept on line 4 will be satisfied if an ADL call is valid, i.e. there is a custom implementation. This is then used on lines 8 and 13 to differentiate between the two overloads. One calls the custom implementation, and the other contains the default implementation. The inline variable on line 18 is in an inline namespace to prevent collision with friend functions in the same namespace. However, because friend functions are only visible to ADL, a fully qualified call will always invoke this function object. \tcblower \cppfile{code_examples/theory/adl_niebloid_code.h} -\end{box-note} +\end{codebox} -This approach has several benefits, in particular on the call site. We no longer need to remember to pull in the namespace of the default implementation. Furthermore, because the call is now fully qualified, we avoid the problem of symbol collisions that can potentially completely prevent ADL. \ No newline at end of file +This approach has several benefits, in particular on the call site. We no longer need to remember to pull in the namespace of the default implementation. Furthermore, because the call is now fully qualified, we avoid the problem of symbol collisions that can potentially completely prevent ADL. + +\section{Integral and floating-point types} +\label{theory:numerics} + +Arguably one of the most error-prone parts of C++ is integral and floating-point expressions. As this part of the language is inherited from C, it relies heavily on fairly complex implicit conversion rules and sometimes interacts unintuitively with more static parts of C++ language. + +\subsection{Integral types} + +There are two phases of potential type changes when working with integral types. First, promotions are applied to types of lower rank than int, and if the resulting expression still contains different integral types, a conversion is applied to arrive at a common type. + +The ranks of integral types are defined in the standard: + +\begin{enumerate} + \item \cpp{bool} + \item \cpp{char}, \cpp{signed char}, \cpp{unsigned char} + \item \cpp{short int}, \cpp{unsigned short int} + \item \cpp{int}, \cpp{unsigned int} + \item \cpp{long int}, \cpp{unsigned long int} + \item \cpp{long long int}, \cpp{unsigned long long int} +\end{enumerate} + +\subsubsection{Promotions} + +As mentioned, integral promotions are applied to types of lower rank than \cpp{int} (e.g. \cpp{bool}, \cpp{char}, \cpp{short}). Such operands will be promoted to int (if \cpp{int} can represent all the values of the type, \cpp{unsigned int} if not). + +Promotions are generally harmless and invisible but can pop up when we mix them with static C++ features (more on that later). + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/P4EGeYxWc}{\ExternalLink}} +\footnotesize Example of \cpp{uint16_t} (both \cpp{a} and \cpp{b}) being promoted to \cpp{int}. +\tcblower +\cppfile{code_examples/theory/integral_promotions.h} +\end{codebox} + +\subsubsection{Conversions} + +Conversions apply after promotions when the two operands are still of different integral types. + +If the types are of the same signedness, the operand of the lower rank is converted to the type of the operand with the higher rank. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/91jhK9xdK}{\ExternalLink}} +\footnotesize Example of integral conversion. Both operands have the same signedness. +\tcblower +\cppfile{code_examples/theory/integral_conversions_same.h} +\end{codebox} + +When we mix integral types of different signedness, there are three possible outcomes. + +When the unsigned operand is of the same or higher rank than the signed operand, the signed operand is converted to the type of the unsigned operand. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/q988MTKor}{\ExternalLink}} +\footnotesize Example of integral conversion. The operands have different signedness but the same rank. +\tcblower +\cppfile{code_examples/theory/integral_conversions_different_a.h} +\end{codebox} + +When the type of the signed operand can represent all values of the unsigned operand, the unsigned operand is converted to the type of the signed operand. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/q988MTKor}{\ExternalLink}} +\footnotesize Example of integral conversion. The operands have different signedness, and the type of the signed operand can represent all values of the unsigned operand. +\tcblower +\cppfile{code_examples/theory/integral_conversions_different_b.h} +\end{codebox} + +Otherwise, both operands are converted to the unsigned version of the signed operand type. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/4hxMGd7Y8}{\ExternalLink}} +\footnotesize Example of integral conversion. The operands have different signedness, and the type of the signed operand cannot represent all values of the unsigned operand. +\tcblower +\cppfile{code_examples/theory/integral_conversions_different_c.h} +\end{codebox} + +Due to these rules, mixing integral types can sometimes lead to non-intuitive behaviour. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/c77aKGGaW}{\ExternalLink}} +\footnotesize Demonstration of a condition that changes value based on the type of one of the operands. +\tcblower +\cppfile{code_examples/theory/integral_conversions_problems.h} +\end{codebox} + +\subsubsection{C++20 safe integral operations} + +The C++20 standard introduced several tools that can be used to mitigate the issues when working with different integral types. + +Firstly, the standard introduced \cpp{std::ssize()}, which allows code that relies on signed integers to avoid mixing signed and unsigned integers when working with containers. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/jej1djd1W}{\ExternalLink}} +\footnotesize Demonstration of using \cpp{std::ssize} to avoid a signed-unsigned mismatch between the index variable and the container size. +\tcblower +\cppfile{code_examples/theory/safe_ssize.h} +\end{codebox} + +Second, a set of safe integral comparisons was introduced to correctly compare values of different integral types (without any value changes caused by conversions). + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/s5ecd1rfh}{\ExternalLink}} +\footnotesize Demonstration of safe comparison functions. +\tcblower +\cppfile{code_examples/theory/safe_compare.h} +\end{codebox} + +Finally, a small utility \cpp{std::in_range} will return whether the tested type can represent the supplied value. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/M3M3bMEYK}{\ExternalLink}} +\footnotesize Demonstration of \cpp{std::in_range}. +\tcblower +\cppfile{code_examples/theory/safe_in_range.h} +\end{codebox} + +\subsection{Floating-point types} + +The rules for floating-point types are a lot simpler. The resulting type of an expression is the highest floating-point type of the two arguments, including situations when one of the arguments is an integral type (highest in order: \cpp{float}, \cpp{double}, \cpp{long double}). + +Importantly, this logic is applied per operator, so ordering matters. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/dvncjjGj6}{\ExternalLink}} +\footnotesize In this example, both expressions end up with the resulting type \cpp{long double}; however, in the first expression, we lose precision by first converting to \cpp{float}. +\tcblower +\cppfile{code_examples/theory/floating_point.h} +\end{codebox} + +Ordering is one of the main things to remember when working with floating-point numbers (this is a general rule, not specific to C++). Operations with floating-point numbers are not associative. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/q3c43h7hj}{\ExternalLink}} +\footnotesize Demonstration of non-associativity of floating point expressions. +\tcblower +\cppfile{code_examples/theory/floating_point_ordering.h} +\end{codebox} + +Any operation with floating-point numbers of different magnitudes should be done with care. + +\subsection{Interactions with other C++ features} + +While integral types are implicitly inter-convertible, references to different integral types are not related types and will, therefore, not bind to each other. This has two consequences. + +First, trying to bind an lvalue reference to a non-matching integral type will not succeed. Second, if the destination reference can bind to temporaries (rvalue, const lvalue), the value will go through an implicit conversion, and the reference will bind to the resulting temporary. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/71b73eo8P}{\ExternalLink}} +\footnotesize Demonstration of integral type behaviour with reference types. +\tcblower +\cppfile{code_examples/theory/references.h} +\end{codebox} + +Finally, we need to talk about type deduction. Because type deduction is a static process, it does remove the opportunity for implicit conversions. However, this also brings potential issues. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/f131811x4}{\ExternalLink}} +\footnotesize Demonstration of potential problem of type deduction when using standard algorithms. +\tcblower +\cppfile{code_examples/theory/deduction_algorithm.h} +\end{codebox} + +But at the same time, when mixed with concepts, we can mitigate implicit conversions while only accepting a specific integral type. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/qcWPPhTT6}{\ExternalLink}} +\footnotesize Demonstration of using concepts to prevent implicit conversions. +\tcblower +\cppfile{code_examples/theory/deduction_concepts.h} +\end{codebox} \ No newline at end of file diff --git a/chapters/XX_index.tex b/chapters/XX_index.tex deleted file mode 100644 index c620e65..0000000 --- a/chapters/XX_index.tex +++ /dev/null @@ -1 +0,0 @@ -\printindex \ No newline at end of file diff --git a/chapters/Y4_TODO.tex b/chapters/Y4_TODO.tex deleted file mode 100644 index fee66c8..0000000 --- a/chapters/Y4_TODO.tex +++ /dev/null @@ -1,16 +0,0 @@ -\chapter{What's next} - -The following chapters are still in the works: - -\begin{itemize} - \item \CC20 ranges in depth - \item \CC20 views - \item writing your own algorithm, writing your own view - \item parallel algorithms and beyond the \CC standard library - \item iterator adapters - \item numerical conversions and promotions (relates to numerical algorithms) - \item \CC23 features -\end{itemize} - -% TODO: Note algorithms that in their range versions only use projection for the functor, not for the output range, such as copy_if. -% TODO: Some views were covered in the transformation chapter re-review. \ No newline at end of file diff --git a/chapters/Y4_making_your_own.tex b/chapters/Y4_making_your_own.tex deleted file mode 100644 index f1073ab..0000000 --- a/chapters/Y4_making_your_own.tex +++ /dev/null @@ -1 +0,0 @@ -\chapter{Implementing custom algorithms} \ No newline at end of file diff --git a/chapters/Y5_parallel_algorithms.tex b/chapters/Y5_parallel_algorithms.tex deleted file mode 100644 index 9f40e28..0000000 --- a/chapters/Y5_parallel_algorithms.tex +++ /dev/null @@ -1 +0,0 @@ -\chapter{Parallel algorithms} \ No newline at end of file diff --git a/chapters/Y8_extra_topics.tex b/chapters/Y8_extra_topics.tex index 80e376b..ed01e48 100644 --- a/chapters/Y8_extra_topics.tex +++ b/chapters/Y8_extra_topics.tex @@ -1,192 +1,9 @@ \chapter{Extras} -\section{A short arithmetic detour} - -The algorithms in the header can have sharp edges. This mainly has to do with the way C++ handles mixed numerical types and how template deduction works. For example, the following is an easy mistake to make: - -\begin{box-note} -\begin{cppcode} -std::vector data{1.1, 2.2, 3.3, 4.4, 5.5}; -auto result = std::accumulate(data.begin(), data.end(), 0); -// result == 15 (actual sum 16.5) -\end{cppcode} -\end{box-note} - -Because we are passing a 0 as the initial value of the accumulator, which is a constant of type int, the accumulator ends up as int. Each fold operation then adds an integer and a double, resulting in a floating-point value. However, it immediately stores it in an integer variable, truncating the value. - -If you want to avoid this problem, you need to be familiar with literal suffixes: - -% TODO: include this information directly in the book, instead of the original links. -\begin{itemize} - \item integer literal suffixes - \item floating-point literal suffixes - \item fixed-size integer literal macros -\end{itemize} - -The second problem arises when mixing different integer types, particularly signed and unsigned integers. Again, as long as you do not mix types, you will not run into issues, but remember that the types that matter are the element types, the initial accumulator value and the functor arguments and return type. - -\begin{box-note} -\begin{cppcode} -std::vector data{1, std::numeric_limits::max()/2}; -auto ok = std::accumulate(data.begin(), data.end(), 0u, - [](auto acc, auto el) { return acc + el; }); -// Always OK, matching types. - -auto impl = std::accumulate(data.begin(), data.end(), 0, - [](auto acc, auto el) { return acc + el; }); -// Implementation defined: -// acc is int -// in acc + el: acc promoted to unsigned, result is unsigned - -// if an unsigned value cannot be represented by the target singed variable -// the behavior is implementation defined - -auto maybe = std::accumulate(data.begin(), data.end(), 0L, - [](auto acc, auto el) { return acc + el; }); -// OK as long as sizeof(long) > sizeof(unsigned) -// acc is long -// in acc + el: el is promoted to long - -// if sizeof(long) > sizeof(unsigned), the long can represent all -// values of unsigned, therefore OK -\end{cppcode} -\end{box-note} - -These are arguably very synthetic examples and shouldn’t appear in production codebases. - -If you are interested in more details about integer conversions and promotions, there is an excellent cheatsheet from hackingcpp on this topic. - -\section{Iterator adapaters} - -We have already used the std::back\_inserter many times across the examples in this series, so let's start with inserter adapters. - -\subsubsection{back\_inserter, front\_inserter, inserter} - -The standard offers three adapters that create instances of back\_insert\_iterator, front\_insert\_iterator and insert\_iterator. When assigned to, these iterators call the corresponding push\_back, push\_front and insert methods on the adapted container. - -\begin{box-note} -\begin{cppcode} -std::list data; -auto back = std::back_inserter(data); -auto front = std::front_inserter(data); - -*back = 10; -// data == { 10 } -*front = 20; -// data == { 20, 10 } - -auto mid = std::inserter(data, std::next(data.begin())); -*mid = 5; -// data == { 20, 5, 10 } -\end{cppcode} -\end{box-note} - -All inserter iterators model only output iterator concept, therefore cannot be used with algorithms that require forward or bidirectional iterators. - -\subsubsection{istream\_iterator, ostream\_iterator} - -These two adapters provide iteration over istream and ostream, respectively. - -The istream\_iterator will read a value by calling the corresponding operator>> when incremented. - -\begin{box-note} -\begin{cppcode} -std::stringstream input("10 20 30 40 50"); -auto in = std::istream_iterator(input); - -int i = *in; // i == 10 -++in; -int j = *in; // j == 20 -\end{cppcode} -\end{box-note} - -The ostream\_iterator will write a value by calling the corresponding operator<< when assigned to. - -\begin{box-note} -\begin{cppcode} -std::stringstream output; -auto out = std::ostream_iterator(output, ", "); - -*out = 10; -*out = 20; -// output == "10, 20," -\end{cppcode} -\end{box-note} - -The additional parameter for ostream\_iterator specifies the divider added after each element. - -The istream\_iterator models an input iterator, and the ostream\_iterator models an output iterator. - -\subsubsection{move\_iterator, make\_move\_iterator} - -The move\_iterator adapter inherits the iterator category from the adapted iterator but will return an rvalue when dereferenced, thus enabling move semantics. - -\begin{box-note} -\begin{cppcode} -std::vector data = { "a", "b", "c" }; -auto it = std::make_move_iterator(data.begin()); - -std::string str(*it); // move construction -++it; -str = *it; // move assignment -// data == { ?, ?, "c" } -// str == "b" -\end{cppcode} -\end{box-note} - -\subsubsection{reverse\_iterator} - -We can use the reverse\_iterator adapter for bidirectional iterators to obtain the iterator for the opposite direction. - -\begin{box-note} -\begin{cppcode} -std::vector data = { 1, 2, 3 }; -auto mid = std::reverse_iterator(data.end()); - -int i = *mid; // i == 3 -++mid; -int j = *mid; // j == 2 -\end{cppcode} -\end{box-note} - -Note that the reversed iterator maps to the previous element in the source direction. Therefore, when reversing begin or rbegin, the result is rend and end, respectively. - -\subsubsection{counted\_iterator} - -When working with non-random-access ranges, it is convenient to specify the range using a begin iterator coupled with the number of elements. The counted\_iterator brings this feature to C++20 ranges without the need for specialised algorithm variants like std::fill\_n. - -\begin{box-note} -\begin{cppcode} -std::list data = { 1, 2, 3, 4, 5 }; - -std::ranges::fill(std::counted_iterator(data.begin(), 3), std::default_sentinel, 9); -// data == { 9, 9, 9, 4, 5 } -\end{cppcode} -\end{box-note} - -The counted\_iterator keeps track of the number of elements, and when the counter reaches zero, the counted\_iterator will compare equal to std::default\_sentinel, signifying the end of the range. - -% TODO: integrate -% Taken from copy section -We can also emulate std::move by using the previously mentioned move\_iterator adapter. - -\begin{box-note} -\begin{cppcode} -std::vector data{ "a", "b", "c", "d", "e", "f"}; - -std::copy(std::make_move_iterator(data.begin()), std::make_move_iterator(data.begin()+3), - data.begin()+3); -// data = { ?, ?, ?, "a", "b", "c" } -\end{cppcode} -\end{box-note} - - \section{Unitialized memory} The heap and uninitialized memory algorithms represent a niche category in the overall algorithm arsenal, primarily because the standard library offers more convenient alternatives that, on the other hand, offer less control. Therefore, if you desire or need the control, you can use these algorithms as a foundation for your custom implementation. - - \subsection{Working with uninitialized memory} The second category of algorithms we will talk about today are algorithms that operate on uninitialized memory. Like the heap algorithms, you should prefer higher-level abstractions (e.g. polymorphic memory resource). However, when working with uninitialized memory, using these algorithms is preferable to implementing all the functionality from scratch. @@ -197,7 +14,7 @@ \subsection{Working with uninitialized memory} Here is an example of allocating (and deallocating) space for ten std::string objects using the global operator new: -\begin{box-note} +\begin{codebox} \begin{cppcode} auto* begin = static_cast( ::operator new[](sizeof(std::string)*10, @@ -205,7 +22,7 @@ \subsection{Working with uninitialized memory} ::operator delete[](begin, static_cast(alignof(std::string))); \end{cppcode} -\end{box-note} +\end{codebox} This example contains a lot of spelling, so let’s step through: @@ -219,12 +36,12 @@ \subsection{Working with uninitialized memory} Here is an example of using a char buffer on the stack: -\begin{box-note} +\begin{codebox} \begin{cppcode} alignas(alignof(std::string)) char buff[sizeof(std::string)*10]; auto *begin = reinterpret_cast(buff); \end{cppcode} -\end{box-note} +\end{codebox} Because the memory lives on the stack, we do not need a deallocation step. diff --git a/code_examples/algorithms/accumulate_code.h b/code_examples/algorithms/accumulate_code.h index aafb1f0..f0e7e85 100644 --- a/code_examples/algorithms/accumulate_code.h +++ b/code_examples/algorithms/accumulate_code.h @@ -1,7 +1,7 @@ std::vector data{1, 2, 3, 4, 5}; - auto sum = std::accumulate(data.begin(), data.end(), 0); // sum == 15 -auto product = std::accumulate(data.begin(), data.end(), 1, std::multiplies<>{}); -// product == 120 +auto product = std::accumulate(data.begin(), data.end(), 1, + std::multiplies<>{}); +// product == 120 \ No newline at end of file diff --git a/code_examples/algorithms/accumulate_right_code.h b/code_examples/algorithms/accumulate_right_code.h index c0f6b22..c62dba0 100644 --- a/code_examples/algorithms/accumulate_right_code.h +++ b/code_examples/algorithms/accumulate_right_code.h @@ -10,5 +10,4 @@ auto right_fold = std::accumulate(data.rbegin(), data.rend(), 0, [](int acc, int el) { return acc / 2 + el; }); -// right_fold == 3 - +// right_fold == 3 \ No newline at end of file diff --git a/code_examples/algorithms/adjacent_difference_code.h b/code_examples/algorithms/adjacent_difference_code.h index c4c1196..34fb6cc 100644 --- a/code_examples/algorithms/adjacent_difference_code.h +++ b/code_examples/algorithms/adjacent_difference_code.h @@ -1,3 +1,3 @@ std::vector data{2, 3, 5, 7, 11, 13}; std::adjacent_difference(data.begin(), data.end(), data.begin()); -// data == {2, 1, 2, 2, 4, 2} +// data == {2, 1, 2, 2, 4, 2} \ No newline at end of file diff --git a/code_examples/algorithms/adjacent_difference_extra_code.h b/code_examples/algorithms/adjacent_difference_extra_code.h index cff7a39..8ccfcbc 100644 --- a/code_examples/algorithms/adjacent_difference_extra_code.h +++ b/code_examples/algorithms/adjacent_difference_extra_code.h @@ -4,4 +4,4 @@ std::vector data(10, 1); std::adjacent_difference(data.begin(), std::prev(data.end()), std::next(data.begin()), std::plus()); // Fibonacci sequence: -// data == {1, 1, 2, 3, 5, 8, 13, 21, 34, 55} +// data == {1, 1, 2, 3, 5, 8, 13, 21, 34, 55} \ No newline at end of file diff --git a/code_examples/algorithms/adjacent_difference_par_code.h b/code_examples/algorithms/adjacent_difference_par_code.h new file mode 100644 index 0000000..9c27055 --- /dev/null +++ b/code_examples/algorithms/adjacent_difference_par_code.h @@ -0,0 +1,6 @@ +std::vector data{2, 3, 5, 7, 11, 13}; +std::vector out(data.size()); + +std::adjacent_difference(std::execution::par_unseq, + data.begin(), data.end(), out.begin()); +// out == {2, 1, 2, 2, 4, 2} \ No newline at end of file diff --git a/code_examples/algorithms/adjacent_find_code.h b/code_examples/algorithms/adjacent_find_code.h index c338b6f..d781286 100644 --- a/code_examples/algorithms/adjacent_find_code.h +++ b/code_examples/algorithms/adjacent_find_code.h @@ -4,4 +4,4 @@ auto it1 = std::adjacent_find(data.begin(), data.end()); auto it2 = std::adjacent_find(data.begin(), data.end(), [](int l, int r) { return l + r > 10; }); -// *it2 == 5, i.e. {5, 6} +// *it2 == 5, i.e. {5, 6} \ No newline at end of file diff --git a/code_examples/algorithms/clamp_code.h b/code_examples/algorithms/clamp_code.h index 43a556c..ca8eb16 100644 --- a/code_examples/algorithms/clamp_code.h +++ b/code_examples/algorithms/clamp_code.h @@ -10,4 +10,4 @@ int c = std::clamp(30, 0, 20); int x = 10, y = 20, z = 30; int &w = const_cast(std::clamp(z, x, y)); w = 99; -// x == 10, y == 99, z == 30 +// x == 10, y == 99, z == 30 \ No newline at end of file diff --git a/code_examples/algorithms/copy_backward_code.h b/code_examples/algorithms/copy_backward_code.h index eda5ca3..965f0fd 100644 --- a/code_examples/algorithms/copy_backward_code.h +++ b/code_examples/algorithms/copy_backward_code.h @@ -6,4 +6,4 @@ std::copy_backward(data.begin(), data.end(), out.end()); // out == {"", "", "", "a", "b", "c", "d", "e", "f"} std::copy_backward(data.begin(), std::prev(data.end()), data.end()); -// data == { "a", "a", "b", "c", "d", "e" } +// data == { "a", "a", "b", "c", "d", "e" } \ No newline at end of file diff --git a/code_examples/algorithms/copy_code.h b/code_examples/algorithms/copy_code.h index c4578b3..daa10e2 100644 --- a/code_examples/algorithms/copy_code.h +++ b/code_examples/algorithms/copy_code.h @@ -5,4 +5,4 @@ std::copy(data.begin(), data.begin()+3, data.begin()+3); // Overlapping case: std::copy(std::next(data.begin()), data.end(), data.begin()); -// data = { "b", "c", "a", "b", "c", "c" } +// data = { "b", "c", "a", "b", "c", "c" } \ No newline at end of file diff --git a/code_examples/algorithms/copy_if_code.h b/code_examples/algorithms/copy_if_code.h index 7bc5b72..23c6fee 100644 --- a/code_examples/algorithms/copy_if_code.h +++ b/code_examples/algorithms/copy_if_code.h @@ -10,4 +10,4 @@ std::ranges::remove_copy_if(data, std::back_inserter(odd), is_even); // odd == { 1, 3, 5, 7, 9 } std::ranges::remove_copy(data, std::back_inserter(no_five), 5); -// no_five == { 1, 2, 3, 4, 6, 7, 8, 9 } +// no_five == { 1, 2, 3, 4, 6, 7, 8, 9 } \ No newline at end of file diff --git a/code_examples/algorithms/copy_n_code.h b/code_examples/algorithms/copy_n_code.h index b316322..3217c88 100644 --- a/code_examples/algorithms/copy_n_code.h +++ b/code_examples/algorithms/copy_n_code.h @@ -2,4 +2,4 @@ std::list data{1, 2, 3, 4, 5, 6, 7, 8, 9}; std::vector out; std::copy_n(data.begin(), 5, std::back_inserter(out)); -// out == { 1, 2, 3, 4, 5 } +// out == { 1, 2, 3, 4, 5 } \ No newline at end of file diff --git a/code_examples/algorithms/equal_with_range_code.h b/code_examples/algorithms/equal_with_range_code.h index 7dffa7f..28a9cc2 100644 --- a/code_examples/algorithms/equal_with_range_code.h +++ b/code_examples/algorithms/equal_with_range_code.h @@ -11,6 +11,4 @@ bool test2 = std::equal(first.begin(), first.end(), auto pair_it = std::mismatch(first.begin(), first.end(), second.begin(), second.end()); // pair_it.first == first.end() -// *pair_it.second == 6 - - +// *pair_it.second == 6 \ No newline at end of file diff --git a/code_examples/algorithms/exclusive_scan_code.h b/code_examples/algorithms/exclusive_scan_code.h index 90abcfd..caa8145 100644 --- a/code_examples/algorithms/exclusive_scan_code.h +++ b/code_examples/algorithms/exclusive_scan_code.h @@ -7,4 +7,4 @@ std::exclusive_scan(src.begin(), src.end(), std::exclusive_scan(src.begin(), src.end(), out.begin(), 1, std::multiplies<>{}); -// out == {1, 1, 2, 6, 24, 120} +// out == {1, 1, 2, 6, 24, 120} \ No newline at end of file diff --git a/code_examples/algorithms/fill_n_code.h b/code_examples/algorithms/fill_n_code.h index b8bc0e0..ba4be01 100644 --- a/code_examples/algorithms/fill_n_code.h +++ b/code_examples/algorithms/fill_n_code.h @@ -6,4 +6,4 @@ std::fill_n(std::back_inserter(data), 5, 11); std::ranges::generate_n(std::back_inserter(data), 5, []() { return 7; }); -// data == {11, 11, 11, 11, 11, 7, 7, 7, 7, 7} +// data == {11, 11, 11, 11, 11, 7, 7, 7, 7, 7} \ No newline at end of file diff --git a/code_examples/algorithms/find_code.h b/code_examples/algorithms/find_code.h index fc0eb5d..504104d 100644 --- a/code_examples/algorithms/find_code.h +++ b/code_examples/algorithms/find_code.h @@ -7,5 +7,4 @@ while ((token = find(it, data.end(), ';')) != data.end()) { std::copy(it, token, std::back_inserter(out.back())); it = std::next(token); } -// out == { "John", "Doe", "April", "1", "1900" } - +// out == { "John", "Doe", "April", "1", "1900" } \ No newline at end of file diff --git a/code_examples/algorithms/find_first_of_code.h b/code_examples/algorithms/find_first_of_code.h index d442027..25c9f7b 100644 --- a/code_examples/algorithms/find_first_of_code.h +++ b/code_examples/algorithms/find_first_of_code.h @@ -3,4 +3,4 @@ std::vector needles = { 7, 5, 3 }; auto it = std::find_first_of(haystack.begin(), haystack.end(), needles.begin(), needles.end()); -// *it == 3, i.e. haystack[2] +// *it == 3, i.e. haystack[2] \ No newline at end of file diff --git a/code_examples/algorithms/find_if_code.h b/code_examples/algorithms/find_if_code.h index ca64255..d1ab486 100644 --- a/code_examples/algorithms/find_if_code.h +++ b/code_examples/algorithms/find_if_code.h @@ -11,4 +11,4 @@ std::copy(begin, [](char c) { return isspace(c); } ).base(), std::back_inserter(out)); -// out == "hello world!" +// out == "hello world!" \ No newline at end of file diff --git a/code_examples/algorithms/for_each_code_parallel.h b/code_examples/algorithms/for_each_code_parallel.h index d0e892f..d7df0ba 100644 --- a/code_examples/algorithms/for_each_code_parallel.h +++ b/code_examples/algorithms/for_each_code_parallel.h @@ -10,5 +10,4 @@ std::for_each(std::execution::par_unseq, data.begin(), data.end(), [](Custom& el) { el.expensive_operation(); - }); - + }); \ No newline at end of file diff --git a/code_examples/algorithms/for_each_code_range.h b/code_examples/algorithms/for_each_code_range.h index 8954667..af7a4be 100644 --- a/code_examples/algorithms/for_each_code_range.h +++ b/code_examples/algorithms/for_each_code_range.h @@ -3,5 +3,4 @@ int sum = 0; for(auto el : data) { sum += el; } -// sum == 45 - +// sum == 45 \ No newline at end of file diff --git a/code_examples/algorithms/for_each_code_simple.h b/code_examples/algorithms/for_each_code_simple.h index ad8c3b9..e5bb075 100644 --- a/code_examples/algorithms/for_each_code_simple.h +++ b/code_examples/algorithms/for_each_code_simple.h @@ -3,5 +3,4 @@ int sum = 0; std::for_each(data.begin(), data.end(), [&sum](int el) { sum += el; }); -// sum == 45 - +// sum == 45 \ No newline at end of file diff --git a/code_examples/algorithms/for_each_n_code.h b/code_examples/algorithms/for_each_n_code.h index 46b70e0..b504025 100644 --- a/code_examples/algorithms/for_each_n_code.h +++ b/code_examples/algorithms/for_each_n_code.h @@ -15,4 +15,4 @@ while (it != final_ranking.end()) { }); page++; it += cnt; -} +} \ No newline at end of file diff --git a/code_examples/algorithms/generate_code.h b/code_examples/algorithms/generate_code.h index 20ecb6f..e0dba25 100644 --- a/code_examples/algorithms/generate_code.h +++ b/code_examples/algorithms/generate_code.h @@ -9,4 +9,4 @@ std::ranges::generate(data, []() { return 5; }); // iota-like std::ranges::generate(data, [i = 0]() mutable { return i++; }); -// data == {0, 1, 2, 3, 4} +// data == {0, 1, 2, 3, 4} \ No newline at end of file diff --git a/code_examples/algorithms/includes_code.h b/code_examples/algorithms/includes_code.h index 4c9c5fa..aa12904 100644 --- a/code_examples/algorithms/includes_code.h +++ b/code_examples/algorithms/includes_code.h @@ -1,8 +1,7 @@ std::vector letters('z'-'a'+1,'\0'); std::iota(letters.begin(), letters.end(), 'a'); - std::string input = "the quick brown fox jumps over the lazy dog"; std::ranges::sort(input); bool test = std::ranges::includes(input, letters); -// test == true +// test == true \ No newline at end of file diff --git a/code_examples/algorithms/inclusive_scan_code.h b/code_examples/algorithms/inclusive_scan_code.h index f444b06..638570e 100644 --- a/code_examples/algorithms/inclusive_scan_code.h +++ b/code_examples/algorithms/inclusive_scan_code.h @@ -7,4 +7,4 @@ std::inclusive_scan(src.begin(), src.end(), std::inclusive_scan(src.begin(), src.end(), out.begin(), std::multiplies<>{}, 1); -// out == {1, 2, 6, 24, 120, 720} +// out == {1, 2, 6, 24, 120, 720} \ No newline at end of file diff --git a/code_examples/algorithms/inner_product_code.h b/code_examples/algorithms/inner_product_code.h index a3f3713..565330c 100644 --- a/code_examples/algorithms/inner_product_code.h +++ b/code_examples/algorithms/inner_product_code.h @@ -3,4 +3,4 @@ std::vector widths{2, 3, 4, 5, 6}; auto total_area = std::inner_product(heights.begin(), heights.end(), widths.begin(), 0); -// total_area == 70 +// total_area == 70 \ No newline at end of file diff --git a/code_examples/algorithms/inner_product_one_code.h b/code_examples/algorithms/inner_product_one_code.h index 5297ecc..ae59f9a 100644 --- a/code_examples/algorithms/inner_product_one_code.h +++ b/code_examples/algorithms/inner_product_one_code.h @@ -5,4 +5,4 @@ auto sum_of_diffs = std::inner_product( std::plus<>{}, [](int left, int right) { return std::abs(left-right); } ); -// sum_of_diffs == 13 +// sum_of_diffs == 13 \ No newline at end of file diff --git a/code_examples/algorithms/iota_code.h b/code_examples/algorithms/iota_code.h index 37fa952..fe32cb2 100644 --- a/code_examples/algorithms/iota_code.h +++ b/code_examples/algorithms/iota_code.h @@ -2,4 +2,4 @@ std::vector data(9, 0); // data == {0, 0, 0, 0, 0, 0, 0, 0, 0} std::iota(data.begin(), data.end(), -4); -// data == {-4, -3, -2, -1, 0, 1, 2, 3, 4} +// data == {-4, -3, -2, -1, 0, 1, 2, 3, 4} \ No newline at end of file diff --git a/code_examples/algorithms/is_partitioned_code.h b/code_examples/algorithms/is_partitioned_code.h index 035a7aa..cc6890c 100644 --- a/code_examples/algorithms/is_partitioned_code.h +++ b/code_examples/algorithms/is_partitioned_code.h @@ -9,4 +9,4 @@ for (int i = 0; i < 16; ++i) { test2 = test2 && std::is_partitioned(data.begin(), data.end(), [&i](int v) { return v < i; }); } -// test2 == true +// test2 == true \ No newline at end of file diff --git a/code_examples/algorithms/is_sorted_until_code.h b/code_examples/algorithms/is_sorted_until_code.h index e550c79..eb55b86 100644 --- a/code_examples/algorithms/is_sorted_until_code.h +++ b/code_examples/algorithms/is_sorted_until_code.h @@ -1,3 +1,3 @@ std::vector data {1, 5, 9, 2, 4, 6}; auto it = std::is_sorted_until(data.begin(), data.end()); -// *it == 2 +// *it == 2 \ No newline at end of file diff --git a/code_examples/algorithms/iter_swap_partition_code.h b/code_examples/algorithms/iter_swap_partition_code.h index 415dee3..b7c4d38 100644 --- a/code_examples/algorithms/iter_swap_partition_code.h +++ b/code_examples/algorithms/iter_swap_partition_code.h @@ -1,7 +1,7 @@ template requires std::forward_iterator && std::indirectly_swappable - && std::predicate + && std::predicate auto partition(It first, It last, Cond cond) { while (first != last && cond(first)) ++first; if (first == last) return last; diff --git a/code_examples/algorithms/iter_swap_unique_ptr_code.h b/code_examples/algorithms/iter_swap_unique_ptr_code.h index 84ac1de..3a6f278 100644 --- a/code_examples/algorithms/iter_swap_unique_ptr_code.h +++ b/code_examples/algorithms/iter_swap_unique_ptr_code.h @@ -5,4 +5,4 @@ int* p2_pre = p2.get(); std::ranges::iter_swap(p1, p2); // p1.get() == p1_pre, *p1 == 2 -// p2.get() == p2_pre, *p2 == 1 +// p2.get() == p2_pre, *p2 == 1 \ No newline at end of file diff --git a/code_examples/algorithms/lexicographical_compare_code.h b/code_examples/algorithms/lexicographical_compare_code.h index bb8c55f..4c090f8 100644 --- a/code_examples/algorithms/lexicographical_compare_code.h +++ b/code_examples/algorithms/lexicographical_compare_code.h @@ -8,7 +8,7 @@ bool cmp1 = std::lexicographical_compare(range1.begin(), range1.end(), bool cmp2 = range1 < range2; // cmp1 == cmp2 == true -bool cmp3 = std::lexicographical_compare(range2.begin(), range2.end(), +bool cmp3 = std::lexicographical_compare(range2.begin(), range2.end(), range3.begin(), range3.end()); // same as bool cmp4 = range2 < range3; diff --git a/code_examples/algorithms/lexicographical_compare_three_way_code.h b/code_examples/algorithms/lexicographical_compare_three_way_code.h index c006bd8..e7ae590 100644 --- a/code_examples/algorithms/lexicographical_compare_three_way_code.h +++ b/code_examples/algorithms/lexicographical_compare_three_way_code.h @@ -1,7 +1,7 @@ -std::vector data1 = { 1, 1, 1}; -std::vector data2 = { 1, 2, 3}; +std::vector data1 = { 1, 1, 1 }; +std::vector data2 = { 1, 2, 3 }; auto cmp = std::lexicographical_compare_three_way( data1.begin(), data1.end(), data2.begin(), data2.end()); -// cmp == std::strong_ordering::less +// cmp == std::strong_ordering::less \ No newline at end of file diff --git a/code_examples/algorithms/lower_bound_code.h b/code_examples/algorithms/lower_bound_code.h index 4579a02..c7d44a9 100644 --- a/code_examples/algorithms/lower_bound_code.h +++ b/code_examples/algorithms/lower_bound_code.h @@ -1,8 +1,10 @@ const std::vector& results = get_results(); -auto lb = std::ranges::lower_bound(results, 49, {}, &ExamResult::score); +auto lb = std::ranges::lower_bound(results, 49, {}, + &ExamResult::score); // First element for which: it->score >= 49 -auto ub = std::ranges::upper_bound(results, 99, {}, &ExamResult::score); +auto ub = std::ranges::upper_bound(results, 99, {}, + &ExamResult::score); // First element for which: 99 < it->score for (auto it = results.begin(); it != lb; it++) { diff --git a/code_examples/algorithms/make_heap_code.h b/code_examples/algorithms/make_heap_code.h index 873cc1a..8685d2b 100644 --- a/code_examples/algorithms/make_heap_code.h +++ b/code_examples/algorithms/make_heap_code.h @@ -12,4 +12,4 @@ std::make_heap(data.begin(), data.end(), std::greater<>{}); // 1 <= 2, 1 <= 3 // 2 <= 4, 2 <= 5 // 3 <= 6, 3 <= 7 -// ... +// ... \ No newline at end of file diff --git a/code_examples/algorithms/merge_code.h b/code_examples/algorithms/merge_code.h index 09ebc0a..9f22b7d 100644 --- a/code_examples/algorithms/merge_code.h +++ b/code_examples/algorithms/merge_code.h @@ -14,5 +14,4 @@ auto cmp = [](const auto& left, const auto& right) std::ranges::merge(data1, data2, std::back_inserter(result), cmp); // result == {0, second}, {1, first}, {2, first}, -// {2, second}, {3, first}, {4, second} - +// {2, second}, {3, first}, {4, second} \ No newline at end of file diff --git a/code_examples/algorithms/min_max_code.h b/code_examples/algorithms/min_max_code.h index 237149e..98a9187 100644 --- a/code_examples/algorithms/min_max_code.h +++ b/code_examples/algorithms/min_max_code.h @@ -3,7 +3,8 @@ int min = std::min(x, y); // min == 10 std::string hello = "hello", world = "world"; -std::string& universe = const_cast(std::max(hello, world)); +std::string& universe = + const_cast(std::max(hello, world)); universe = "universe"; std::string greeting = hello + " " + world; diff --git a/code_examples/algorithms/minmax_code.h b/code_examples/algorithms/minmax_code.h index 5028c25..a42fa27 100644 --- a/code_examples/algorithms/minmax_code.h +++ b/code_examples/algorithms/minmax_code.h @@ -9,4 +9,4 @@ auto [first, second] = std::minmax(b, a); // Operating on prvalues requires capture by value std::pair result = std::minmax(5, 10); -// result.first = 5, result.second = 10 +// result.first = 5, result.second = 10 \ No newline at end of file diff --git a/code_examples/algorithms/minmax_extra_code.h b/code_examples/algorithms/minmax_extra_code.h index 2b6b23e..b8d6fd5 100644 --- a/code_examples/algorithms/minmax_extra_code.h +++ b/code_examples/algorithms/minmax_extra_code.h @@ -6,4 +6,4 @@ auto minmax = std::minmax({5, 3, -2, 0}); std::list data{5, 3, -2, 0}; auto max = std::ranges::max(data); -// max == 5 +// max == 5 \ No newline at end of file diff --git a/code_examples/algorithms/mismatch_code.h b/code_examples/algorithms/mismatch_code.h index 3e0666b..ac7721c 100644 --- a/code_examples/algorithms/mismatch_code.h +++ b/code_examples/algorithms/mismatch_code.h @@ -1,5 +1,6 @@ std::vector first = { 1, 2, 3, 4, 5 }; std::vector second = { 1, 2, 2, 4, 5 }; -auto it_pair = std::mismatch(first.begin(), first.end(), second.begin()); -// *it_pair.first == 3, *it_pair.second == 2 +auto it_pair = std::mismatch(first.begin(), first.end(), + second.begin()); +// *it_pair.first == 3, *it_pair.second == 2 \ No newline at end of file diff --git a/code_examples/algorithms/move_code.h b/code_examples/algorithms/move_code.h index 9eb5616..5f0ecd9 100644 --- a/code_examples/algorithms/move_code.h +++ b/code_examples/algorithms/move_code.h @@ -2,4 +2,5 @@ std::vector data{ "a", "b", "c", "d", "e", "f"}; std::move(data.begin(), data.begin()+3, data.begin()+3); // data = { ?, ?, ?, "a", "b", "c" } -// Note: most implementations will set moved-out-of std::string to empty. +// Note: most implementations will set +// a moved-out-of std::string to empty. \ No newline at end of file diff --git a/code_examples/algorithms/nth_element_code.h b/code_examples/algorithms/nth_element_code.h index 53de271..a742938 100644 --- a/code_examples/algorithms/nth_element_code.h +++ b/code_examples/algorithms/nth_element_code.h @@ -1,8 +1,7 @@ std::vector data{9, 1, 8, 2, 7, 3, 6, 4, 5}; -std::nth_element(data.begin(), data.begin()+4, data.end()); -// data[4] == 5 -// data[0..3] == {1, 2, 3, 4} - in undeterminate order +std::nth_element(data.begin(), data.begin() + 4, data.end()); +// data[4] == 5, data[0..3] < data[4] -std::nth_element(data.begin(), data.begin()+7, data.end(), std::greater<>()); -// data[7] == 2 -// data[0..6] == {3, 4, 5, 6, 7, 8, 9} - in undeterminate order +std::nth_element(data.begin(), data.begin() + 7, data.end(), + std::greater<>()); +// data[7] == 2, data[0..6] > data[7] \ No newline at end of file diff --git a/code_examples/algorithms/partial_sort_code.h b/code_examples/algorithms/partial_sort_code.h index ae55eef..d150b76 100644 --- a/code_examples/algorithms/partial_sort_code.h +++ b/code_examples/algorithms/partial_sort_code.h @@ -3,4 +3,4 @@ std::partial_sort(data.begin(), data.begin()+3, data.end()); // data == {1, 2, 3, -unspecified order-} std::ranges::partial_sort(data, data.begin()+3, std::greater<>()); -// data == {9, 8, 7, -unspecified order-} +// data == {9, 8, 7, -unspecified order-} \ No newline at end of file diff --git a/code_examples/algorithms/partial_sort_copy_code.h b/code_examples/algorithms/partial_sort_copy_code.h index 034ec1b..38b8724 100644 --- a/code_examples/algorithms/partial_sort_copy_code.h +++ b/code_examples/algorithms/partial_sort_copy_code.h @@ -7,5 +7,4 @@ auto cnt = std::counted_iterator(input, 10); std::ranges::partial_sort_copy(cnt, std::default_sentinel, top.begin(), top.end(), std::greater<>{}); -// top == { 9, 8, 7 } - +// top == { 9, 8, 7 } \ No newline at end of file diff --git a/code_examples/algorithms/partial_sum_code.h b/code_examples/algorithms/partial_sum_code.h index d5d921f..232f57b 100644 --- a/code_examples/algorithms/partial_sum_code.h +++ b/code_examples/algorithms/partial_sum_code.h @@ -7,4 +7,4 @@ std::partial_sum(data.begin(), data.end(), data.begin()); std::vector out; std::partial_sum(data.begin(), data.end(), std::back_inserter(out), std::multiplies<>{}); -// out == {1, 2, 6, 24, 120, 720} +// out == {1, 2, 6, 24, 120, 720} \ No newline at end of file diff --git a/code_examples/algorithms/partition_code.h b/code_examples/algorithms/partition_code.h index ef80750..3123eff 100644 --- a/code_examples/algorithms/partition_code.h +++ b/code_examples/algorithms/partition_code.h @@ -12,4 +12,4 @@ for (auto it = results.begin(); it != pp; ++it) { // process failed students for (auto it = pp; it != results.end(); ++it) { std::cout << "[FAIL] " << it->student_name << "\n"; -} +} \ No newline at end of file diff --git a/code_examples/algorithms/partition_copy_code.h b/code_examples/algorithms/partition_copy_code.h index a37cbd9..c87dcb3 100644 --- a/code_examples/algorithms/partition_copy_code.h +++ b/code_examples/algorithms/partition_copy_code.h @@ -8,4 +8,4 @@ std::partition_copy(data.begin(), data.end(), is_even); // even == {2, 4, 6} -// odd == {1, 3, 5} +// odd == {1, 3, 5} \ No newline at end of file diff --git a/code_examples/algorithms/push_heap_code.h b/code_examples/algorithms/push_heap_code.h index bdea941..ff5b164 100644 --- a/code_examples/algorithms/push_heap_code.h +++ b/code_examples/algorithms/push_heap_code.h @@ -17,4 +17,4 @@ std::push_heap(data.begin(), data.end()); std::pop_heap(data.begin(), data.end()); // data == { [heap_part], 9 } std::pop_heap(data.begin(), std::prev(data.end())); -// data == { [heap_part], 7, 9 } +// data == { [heap_part], 7, 9 } \ No newline at end of file diff --git a/code_examples/algorithms/qsort_code.h b/code_examples/algorithms/qsort_code.h index d4a9979..f718ac7 100644 --- a/code_examples/algorithms/qsort_code.h +++ b/code_examples/algorithms/qsort_code.h @@ -10,4 +10,4 @@ qsort(data, size, sizeof(int), if (vl > vr) return 1; return 0; }); -// data == {-8, -1, 1, 2, 7, 9} +// data == {-8, -1, 1, 2, 7, 9} \ No newline at end of file diff --git a/code_examples/algorithms/qsort_not_code.h b/code_examples/algorithms/qsort_not_code.h index 663cc85..e196485 100644 --- a/code_examples/algorithms/qsort_not_code.h +++ b/code_examples/algorithms/qsort_not_code.h @@ -2,4 +2,4 @@ int data[] = {2, 1, 9, -1, 7, -8}; int size = sizeof data / sizeof(int); std::sort(&data[0], &data[size], std::less<>()); -// data == {-8, -1, 1, 2, 7, 9} +// data == {-8, -1, 1, 2, 7, 9} \ No newline at end of file diff --git a/code_examples/algorithms/reduce_code.h b/code_examples/algorithms/reduce_code.h index 6001d4c..9b20907 100644 --- a/code_examples/algorithms/reduce_code.h +++ b/code_examples/algorithms/reduce_code.h @@ -7,9 +7,10 @@ sum = std::reduce(std::execution::par_unseq, data.begin(), data.end(), 0); // sum == 15 -auto product = std::reduce(data.begin(), data.end(), 1, std::multiplies<>{}); +auto product = std::reduce(data.begin(), data.end(), 1, + std::multiplies<>{}); // product == 120 product = std::reduce(std::execution::par_unseq, data.begin(), data.end(), 1, std::multiplies<>{}); -// product == 120 +// product == 120 \ No newline at end of file diff --git a/code_examples/algorithms/reduce_noinit_code.h b/code_examples/algorithms/reduce_noinit_code.h index adb74d8..0b9262e 100644 --- a/code_examples/algorithms/reduce_noinit_code.h +++ b/code_examples/algorithms/reduce_noinit_code.h @@ -7,4 +7,4 @@ struct Duck { std::vector data(2, Duck{}); Duck final_duck = std::reduce(data.begin(), data.end()); -// final_duck.sound == "QuackQuackQuack" +// final_duck.sound == "QuackQuackQuack" \ No newline at end of file diff --git a/code_examples/algorithms/replace_copy_code.h b/code_examples/algorithms/replace_copy_code.h index d876270..04b8177 100644 --- a/code_examples/algorithms/replace_copy_code.h +++ b/code_examples/algorithms/replace_copy_code.h @@ -5,5 +5,6 @@ std::ranges::replace_copy(data, std::back_inserter(out), 5, 10); // out == { 1, 2, 3, 4, 10, 6, 7, 8, 9 } auto is_even = [](int v) { return v % 2 == 0; }; -std::ranges::replace_copy_if(data, std::back_inserter(odd), is_even, -1); -// odd == { 1, -1, 3, -1, 5, -1, 7, -1, 9 } +std::ranges::replace_copy_if(data, std::back_inserter(odd), + is_even, -1); +// odd == { 1, -1, 3, -1, 5, -1, 7, -1, 9 } \ No newline at end of file diff --git a/code_examples/algorithms/reverse_copy_code.h b/code_examples/algorithms/reverse_copy_code.h index c456309..7c8eed1 100644 --- a/code_examples/algorithms/reverse_copy_code.h +++ b/code_examples/algorithms/reverse_copy_code.h @@ -2,4 +2,4 @@ std::vector data{1, 2, 3, 4, 5, 6, 7, 8, 9}; std::vector out; std::ranges::reverse_copy(data, std::back_inserter(out)); -// out == { 9, 8, 7, 6, 5, 4, 3, 2, 1 } +// out == { 9, 8, 7, 6, 5, 4, 3, 2, 1 } \ No newline at end of file diff --git a/code_examples/algorithms/rotate_copy_code.h b/code_examples/algorithms/rotate_copy_code.h index 9316025..b692e4d 100644 --- a/code_examples/algorithms/rotate_copy_code.h +++ b/code_examples/algorithms/rotate_copy_code.h @@ -1,5 +1,6 @@ std::vector data{1, 2, 3, 4, 5, 6, 7, 8, 9}; std::vector out; -std::ranges::rotate_copy(data, data.begin()+4, std::back_inserter(out)); -// out == { 5, 6, 7, 8, 9, 1, 2, 3, 4 } +std::ranges::rotate_copy(data, data.begin() + 4, + std::back_inserter(out)); +// out == { 5, 6, 7, 8, 9, 1, 2, 3, 4 } \ No newline at end of file diff --git a/code_examples/algorithms/search_code.h b/code_examples/algorithms/search_code.h index bb3d4d4..6dbe535 100644 --- a/code_examples/algorithms/search_code.h +++ b/code_examples/algorithms/search_code.h @@ -7,4 +7,4 @@ auto it1 = std::search(haystack.begin(), haystack.end(), auto it2 = std::find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); -// it2..end == "bba" +// it2..end == "bba" \ No newline at end of file diff --git a/code_examples/algorithms/search_n_code.h b/code_examples/algorithms/search_n_code.h index b057854..fa45a49 100644 --- a/code_examples/algorithms/search_n_code.h +++ b/code_examples/algorithms/search_n_code.h @@ -8,4 +8,4 @@ auto it2 = std::search_n(data.begin(), data.end(), 3, 3, // *it2 == 8, i.e. {8, 3, 3} auto it3 = std::search_n(data.begin(), data.end(), 2, 0); -// it3 == data.end(), i.e. not found +// it3 == data.end(), i.e. not found \ No newline at end of file diff --git a/code_examples/algorithms/searchers_code.h b/code_examples/algorithms/searchers_code.h index 5b0b2f5..816ce43 100644 --- a/code_examples/algorithms/searchers_code.h +++ b/code_examples/algorithms/searchers_code.h @@ -9,6 +9,4 @@ auto it2 = std::search(haystack.begin(), haystack.end(), auto it3 = std::search(haystack.begin(), haystack.end(), std::boyer_moore_horspool_searcher(needle.begin(), needle.end())); -// it1 == it2 == it3 - - +// it1 == it2 == it3 \ No newline at end of file diff --git a/code_examples/algorithms/set_difference_equal_code.h b/code_examples/algorithms/set_difference_equal_code.h index 11dadd5..1af7d82 100644 --- a/code_examples/algorithms/set_difference_equal_code.h +++ b/code_examples/algorithms/set_difference_equal_code.h @@ -7,7 +7,8 @@ auto cmp = [](const auto& l, const auto& r) { return l.value < r.value; }; -std::vector equal1{{"first_a", 1}, {"first_b", 1}, {"first_c", 1}, {"first_d", 1}}; +std::vector equal1{{"first_a", 1}, {"first_b", 1}, + {"first_c", 1}, {"first_d", 1}}; std::vector equal2{{"second_a", 1}, {"second_b", 1}}; std::vector equal_difference; diff --git a/code_examples/algorithms/set_intersection_equal_code.h b/code_examples/algorithms/set_intersection_equal_code.h index 0789c4d..4595f55 100644 --- a/code_examples/algorithms/set_intersection_equal_code.h +++ b/code_examples/algorithms/set_intersection_equal_code.h @@ -8,7 +8,8 @@ auto cmp = [](const auto& l, const auto& r) { }; std::vector equal1{{"first_a", 1}, {"first_b", 2}}; -std::vector equal2{{"second_a", 1}, {"second_b", 2}, {"second_c", 2}}; +std::vector equal2{{"second_a", 1}, {"second_b", 2}, + {"second_c", 2}}; std::vector intersection; std::ranges::set_intersection(equal1, equal2, diff --git a/code_examples/algorithms/set_symmetric_difference_equal_code.h b/code_examples/algorithms/set_symmetric_difference_equal_code.h index 239703e..877bcd3 100644 --- a/code_examples/algorithms/set_symmetric_difference_equal_code.h +++ b/code_examples/algorithms/set_symmetric_difference_equal_code.h @@ -7,12 +7,12 @@ auto cmp = [](const auto& l, const auto& r) { return l.value < r.value; }; -std::vector equal1{{"first_a", 1}, {"first_b", 2}, {"first_c", 2}}; -std::vector equal2{{"second_a", 1}, {"second_b", 1}, {"second_c", 2}}; +std::vector equal1{{"first_a", 1}, {"first_b", 2}, + {"first_c", 2}}; +std::vector equal2{{"second_a", 1}, {"second_b", 1}, + {"second_c", 2}}; std::vector equal_symmetric_difference; std::ranges::set_symmetric_difference(equal1, equal2, std::back_inserter(equal_symmetric_difference), cmp); -// equal_symmetric_difference == { {"second_b", 1}, {"first_c", 2} } - - +// equal_symmetric_difference == { {"second_b", 1}, {"first_c", 2} } \ No newline at end of file diff --git a/code_examples/algorithms/set_union_code.h b/code_examples/algorithms/set_union_code.h index c976849..4736d8c 100644 --- a/code_examples/algorithms/set_union_code.h +++ b/code_examples/algorithms/set_union_code.h @@ -4,4 +4,4 @@ std::vector data2{2, 4, 6}; std::vector set_union; std::ranges::set_union(data1, data2, std::back_inserter(set_union)); -// set_union == { 1, 2, 3, 4, 5, 6 } +// set_union == { 1, 2, 3, 4, 5, 6 } \ No newline at end of file diff --git a/code_examples/algorithms/set_union_equal_code.h b/code_examples/algorithms/set_union_equal_code.h index b10c984..5ff2300 100644 --- a/code_examples/algorithms/set_union_equal_code.h +++ b/code_examples/algorithms/set_union_equal_code.h @@ -7,10 +7,13 @@ auto cmp = [](const auto& l, const auto& r) { return l.value < r.value; }; -std::vector equal1{{"first_a", 1}, {"first_b", 1}, {"first_c", 2}}; -std::vector equal2{{"second_a", 1}, {"second_b", 2}, {"second_c", 2}}; +std::vector equal1{{"first_a", 1}, {"first_b", 1}, + {"first_c", 2}}; +std::vector equal2{{"second_a", 1}, {"second_b", 2}, + {"second_c", 2}}; std::vector equal_union; std::ranges::set_union(equal1, equal2, std::back_inserter(equal_union), cmp); -// equal_union == { {"first_a", 1}, {"first_b", 1}, {"first_c", 2}, {"second_c", 2} } +// equal_union == { {"first_a", 1}, {"first_b", 1}, +// {"first_c", 2}, {"second_c", 2} } diff --git a/code_examples/algorithms/shift_code.h b/code_examples/algorithms/shift_code.h new file mode 100644 index 0000000..f61c4db --- /dev/null +++ b/code_examples/algorithms/shift_code.h @@ -0,0 +1,7 @@ +std::vector data{1,2,3,4,5,6,7,8,9}; +std::shift_left(data.begin(), data.end(), 3); +// data == {4, 5, 6, 7, 8, 9, 7, 8, 9} + +data = {1,2,3,4,5,6,7,8,9}; +std::shift_right(data.begin(), data.end(), 3); +// data == {1, 2, 3, 1, 2, 3, 4, 5, 6} \ No newline at end of file diff --git a/code_examples/algorithms/shift_move_code.h b/code_examples/algorithms/shift_move_code.h new file mode 100644 index 0000000..a5889ad --- /dev/null +++ b/code_examples/algorithms/shift_move_code.h @@ -0,0 +1,20 @@ +struct EmptyOnMove { + char value; + EmptyOnMove(char value) : value(value) {} + EmptyOnMove(EmptyOnMove&& src) : + value(std::exchange(src.value,'-')) {} + EmptyOnMove& operator=(EmptyOnMove&& src) { + value = std::exchange(src.value, '-'); + return *this; + } + EmptyOnMove(const EmptyOnMove&) = default; + EmptyOnMove& operator=(const EmptyOnMove&) = default; +}; + +int main() { + std::vector nontrivial{ + {'a'},{'b'},{'c'},{'d'},{'e'},{'f'},{'g'}}; + + std::shift_right(nontrivial.begin(), nontrivial.end(), 4); + // nontrivial == { '-', '-', '-', 'd', 'a', 'b', 'c' } +} \ No newline at end of file diff --git a/code_examples/algorithms/shuffle_code.h b/code_examples/algorithms/shuffle_code.h index 9c40a62..cf075a3 100644 --- a/code_examples/algorithms/shuffle_code.h +++ b/code_examples/algorithms/shuffle_code.h @@ -9,7 +9,8 @@ friend std::ostream& operator << (std::ostream& s, const Card& card) { {"Hearts", "Diamonds", "Clubs", "Spades"}; if (card.index >= 52) - throw std::domain_error("Card index has to be in the range 0..51"); + throw std::domain_error( + "Card index has to be in the range 0..51"); s << ranks[card.index%13] << " of " << suits[card.index/13]; return s; diff --git a/code_examples/algorithms/sort_code.h b/code_examples/algorithms/sort_code.h index ec74321..d8e9df1 100644 --- a/code_examples/algorithms/sort_code.h +++ b/code_examples/algorithms/sort_code.h @@ -5,4 +5,4 @@ std::sort(data1.begin(), data1.end()); std::list data2 = {9, 1, 8, 2, 7, 3, 6, 4, 5}; // std::sort(data.begin(), data.end()); // doesn't compile data2.sort(); -// data2 == {1, 2, 3, 4, 5, 6, 7, 8, 9} +// data2 == {1, 2, 3, 4, 5, 6, 7, 8, 9} \ No newline at end of file diff --git a/code_examples/algorithms/sort_heap_code.h b/code_examples/algorithms/sort_heap_code.h index 624c467..dadde1b 100644 --- a/code_examples/algorithms/sort_heap_code.h +++ b/code_examples/algorithms/sort_heap_code.h @@ -4,4 +4,4 @@ std::make_heap(data.begin(), data.end()); // data == {9, 8, 7, 4, 5, 6, 3, 2, 1} - different ordering possible std::sort_heap(data.begin(), data.end()); -// data == {1, 2, 3, 4, 5, 6, 7, 8, 9} +// data == {1, 2, 3, 4, 5, 6, 7, 8, 9} \ No newline at end of file diff --git a/code_examples/algorithms/sort_projection_code.h b/code_examples/algorithms/sort_projection_code.h index 9efe63f..076848e 100644 --- a/code_examples/algorithms/sort_projection_code.h +++ b/code_examples/algorithms/sort_projection_code.h @@ -5,4 +5,4 @@ struct Account { std::vector accounts{{0.1}, {0.3}, {0.01}, {0.05}}; std::ranges::sort(accounts, std::greater<>{}, &Account::value); -// accounts = { {0.3}, {0.1}, {0.05}, {0.01} } +// accounts = { {0.3}, {0.1}, {0.05}, {0.01} } \ No newline at end of file diff --git a/code_examples/algorithms/stable_partition_code.h b/code_examples/algorithms/stable_partition_code.h index 99d5b22..f51e0be 100644 --- a/code_examples/algorithms/stable_partition_code.h +++ b/code_examples/algorithms/stable_partition_code.h @@ -1,2 +1,2 @@ auto& widget = get_widget(); -std::ranges::stable_partition(widget.items, &Item::is_selected); +std::ranges::stable_partition(widget.items, &Item::is_selected); \ No newline at end of file diff --git a/code_examples/algorithms/stable_sort_code.h b/code_examples/algorithms/stable_sort_code.h index c0141ad..7c59dd2 100644 --- a/code_examples/algorithms/stable_sort_code.h +++ b/code_examples/algorithms/stable_sort_code.h @@ -3,8 +3,9 @@ struct Record { int rank; }; -std::vector data {{"q", 1}, {"f", 1}, {"c", 2}, {"a", 1}, {"d", 3}}; +std::vector data {{"q", 1}, {"f", 1}, {"c", 2}, + {"a", 1}, {"d", 3}}; -std::ranges::stable_sort(data, {}, &Record::label); +std::ranges::stable_sort(data, {}, &Record::label); std::ranges::stable_sort(data, {}, &Record::rank); -// Guarantted order: a-1, f-1, q-1, c-2, d-3 +// Guarantted order: a-1, f-1, q-1, c-2, d-3 \ No newline at end of file diff --git a/code_examples/algorithms/swap_calling_code.h b/code_examples/algorithms/swap_calling_code.h index b240385..0530bd0 100644 --- a/code_examples/algorithms/swap_calling_code.h +++ b/code_examples/algorithms/swap_calling_code.h @@ -1,4 +1,4 @@ void some_algorithm(auto& x, auto& y) { using std::swap; swap(x, y); -} +} \ No newline at end of file diff --git a/code_examples/algorithms/swap_ranges_code.h b/code_examples/algorithms/swap_ranges_code.h index e475d3b..2faf516 100644 --- a/code_examples/algorithms/swap_ranges_code.h +++ b/code_examples/algorithms/swap_ranges_code.h @@ -1,3 +1,3 @@ std::vector data{ 1, 2, 3, 4, 5, 6, 7, 8, 9}; std::swap_ranges(data.begin(), data.begin()+3, data.rbegin()); -// data = { 9, 8, 7, 4, 5, 6, 3, 2, 1 } +// data = { 9, 8, 7, 4, 5, 6, 3, 2, 1 } \ No newline at end of file diff --git a/code_examples/algorithms/transform_inclusive_code.h b/code_examples/algorithms/transform_inclusive_code.h index a93ab69..036e6ea 100644 --- a/code_examples/algorithms/transform_inclusive_code.h +++ b/code_examples/algorithms/transform_inclusive_code.h @@ -9,4 +9,4 @@ std::vector out2; std::transform_inclusive_scan(data.begin(), data.end(), std::back_inserter(out2), std::plus<>{}, [](int v) { return std::abs(v); }); -// out2 == {10, 13, 15, 20, 26} +// out2 == {10, 13, 15, 20, 26} \ No newline at end of file diff --git a/code_examples/algorithms/transform_reduce_code.h b/code_examples/algorithms/transform_reduce_code.h index 5ce78b6..aab02cd 100644 --- a/code_examples/algorithms/transform_reduce_code.h +++ b/code_examples/algorithms/transform_reduce_code.h @@ -6,4 +6,4 @@ auto sum_of_squares = std::transform_reduce(data.begin(), data.end(), std::vector coef{1, -1, 1, -1, 1}; auto result = std::transform_reduce(data.begin(), data.end(), coef.begin(), 0); -// result == 1*1 + 2*(-1) + 3*1 + 4*(-1) + 5*1 == 3 +// result == 1*1 + 2*(-1) + 3*1 + 4*(-1) + 5*1 == 3 \ No newline at end of file diff --git a/code_examples/algorithms/uninitialized_constr_code.h b/code_examples/algorithms/uninitialized_constr_code.h index 514edcc..ca0192e 100644 --- a/code_examples/algorithms/uninitialized_constr_code.h +++ b/code_examples/algorithms/uninitialized_constr_code.h @@ -2,15 +2,12 @@ alignas(alignof(std::string)) char buffer[sizeof(std::string)*10]; auto *begin = reinterpret_cast(buffer); auto *it = begin; -std::uninitialized_default_construct_n(it, 3); -it += 3; -std::uninitialized_fill_n(it, 2, "Hello World!"); -it += 2; -std::uninitialized_value_construct_n(it, 3); -it += 3; -std::uninitialized_fill_n(it, 2, "Bye World!"); +it = std::uninitialized_default_construct_n(it, 3); +it = std::uninitialized_fill_n(it, 2, "Hello World!"); +it = std::uninitialized_value_construct_n(it, 3); +it = std::uninitialized_fill_n(it, 2, "Bye World!"); -// {"", "", "", "Hello World!", "Hello World!", "", "", "", "Bye World!", "Bye World!"} - -std::destroy_n(begin, 10); +// {"", "", "", "Hello World!", "Hello World!", "", "", "", +// "Bye World!", "Bye World!"} +std::destroy_n(begin, 10); \ No newline at end of file diff --git a/code_examples/algorithms/uninitialized_copy_code.h b/code_examples/algorithms/uninitialized_copy_code.h index e66cec0..3f7ebee 100644 --- a/code_examples/algorithms/uninitialized_copy_code.h +++ b/code_examples/algorithms/uninitialized_copy_code.h @@ -1,6 +1,7 @@ alignas(alignof(std::string)) char buff1[sizeof(std::string)*5]; alignas(alignof(std::string)) char buff2[sizeof(std::string)*5]; -std::vector data = {"hello", "world", "and", "everyone", "else"}; +std::vector data = { + "hello", "world", "and", "everyone", "else"}; auto *bg1 = reinterpret_cast(buff1); std::uninitialized_copy(data.begin(), data.end(), bg1); diff --git a/code_examples/ranges/borrowed_optin_code.h b/code_examples/ranges/borrowed_optin_code.h index f80215a..122ba93 100644 --- a/code_examples/ranges/borrowed_optin_code.h +++ b/code_examples/ranges/borrowed_optin_code.h @@ -1,2 +1,2 @@ -template -inline constexpr bool std::ranges::enable_borrowed_range> = true; +template inline constexpr bool + std::ranges::enable_borrowed_range> = true; diff --git a/code_examples/ranges/projected_type_code.h b/code_examples/ranges/projected_type_code.h index b74b879..3a4f614 100644 --- a/code_examples/ranges/projected_type_code.h +++ b/code_examples/ranges/projected_type_code.h @@ -7,9 +7,9 @@ std::vector out1; // std::vector out1; would not compile std::ranges::copy_if(data, std::back_inserter(out1), [](B) { return true; }, // predicate accepts B - [](A) { return B{}; }); // projection projects A->B for the predicate only + [](A) { return B{}; }); // projection projects A->B std::vector out2; std::ranges::transform(data, std::back_inserter(out2), [](auto x) { return x; }, // no-op transformation functor - [](A) { return B{}; }); // projection projects A->B for the functor only + [](A) { return B{}; }); // projection projects A->B diff --git a/code_examples/ranges/stateful_code.h b/code_examples/ranges/stateful_code.h new file mode 100644 index 0000000..2c622ba --- /dev/null +++ b/code_examples/ranges/stateful_code.h @@ -0,0 +1,22 @@ +std::list data{1, 2, 3, 4, 5, 6, 7, 8}; +auto view = data | std::views::drop(3); + +for (auto v : view) { + // iterate over: 4, 5, 6, 7, 8 +} + +// Note, if we used std::vector, push could invalidate +// the cached iterator inside of std::views::drop. +data.push_front(99); +for (auto v : view) { + // iterate over: 4, 5, 6, 7, 8 +} + +// Fresh view +for (auto v : data | std::views::drop(3)) { + // iterate over: 3, 4, 5, 6, 7, 8 +} + +const auto view2 = data | std::views::drop(3); +// for (auto v : view2) {} +// Wouldn't compile, std::views::drop requires mutability. \ No newline at end of file diff --git a/code_examples/theory/deduction_algorithm.h b/code_examples/theory/deduction_algorithm.h new file mode 100644 index 0000000..d46d1d2 --- /dev/null +++ b/code_examples/theory/deduction_algorithm.h @@ -0,0 +1,8 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7, 8, 9}; + +auto v = std::accumulate(data.begin(), data.end(), 0); +// 0 is a literal of type int. Internally this means that +// the accumulator (and result) type of the algorithm will be +// int, despite iterating over a container of type unsigned. + +// v == 45, decltype(v) == int \ No newline at end of file diff --git a/code_examples/theory/deduction_concepts.h b/code_examples/theory/deduction_concepts.h new file mode 100644 index 0000000..f3061bf --- /dev/null +++ b/code_examples/theory/deduction_concepts.h @@ -0,0 +1,5 @@ +template concept IsInt = std::same_as; +void function(const IsInt auto&) {} + +function(0); // OK +// function(0u); // will fail to compile, deduced type unsigned \ No newline at end of file diff --git a/code_examples/theory/floating_point.h b/code_examples/theory/floating_point.h new file mode 100644 index 0000000..35e6b99 --- /dev/null +++ b/code_examples/theory/floating_point.h @@ -0,0 +1,9 @@ +auto src = UINT64_MAX - UINT32_MAX; +auto m = (1.0f * src) * 1.0L; +auto n = 1.0f * (src * 1.0L); +// decltype(m) == decltype(n) == long double + +std::cout << std::fixed << m << "\n" << n << "\n" << src << "\n"; +// 18446744073709551616.000000 +// 18446744069414584320.000000 +// 18446744069414584320 \ No newline at end of file diff --git a/code_examples/theory/floating_point_ordering.h b/code_examples/theory/floating_point_ordering.h new file mode 100644 index 0000000..b57653f --- /dev/null +++ b/code_examples/theory/floating_point_ordering.h @@ -0,0 +1,19 @@ +float v = 1.0f; +float next = std::nextafter(v, 2.0f); +// next is the next higher floating pointer number +float diff = (next-v)/2; +// diff is below the resolution of float +// importantly: v + diff == v + +std::vector data1(100, diff); +data1.front() = v; // data1 == { v, ... } +float r1 = std::accumulate(data1.begin(), data1.end(), 0.f); +// r1 == v +// we added diff 99 times, but each time, the value did not change + +std::vector data2(100, diff); +data2.back() = v; // data2 == { ..., v } +float r2 = std::accumulate(data2.begin(), data2.end(), 0.f); +// r2 != v +// we added diff 99 times, but we did that before adding to v +// the sum of 99 diffs is above the resolution threshold \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_different_a.h b/code_examples/theory/integral_conversions_different_a.h new file mode 100644 index 0000000..efedfb3 --- /dev/null +++ b/code_examples/theory/integral_conversions_different_a.h @@ -0,0 +1,5 @@ +int a = -100; +unsigned b = 0; + +auto v = a + b; +// v ~ -100 + (UINT_MAX + 1), decltype(v) == unsigned \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_different_b.h b/code_examples/theory/integral_conversions_different_b.h new file mode 100644 index 0000000..b0cab72 --- /dev/null +++ b/code_examples/theory/integral_conversions_different_b.h @@ -0,0 +1,5 @@ +unsigned a = 100; +long int b = -200; + +auto v = a + b; +// v = -100, decltype(v) == long int \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_different_c.h b/code_examples/theory/integral_conversions_different_c.h new file mode 100644 index 0000000..fdc56b8 --- /dev/null +++ b/code_examples/theory/integral_conversions_different_c.h @@ -0,0 +1,5 @@ +long long a = -100; +unsigned long b = 0; // assuming sizeof(long) == sizeof(long long) + +auto v = a + b; +// v ~ -100 + (ULLONG_MAX + 1), decltype(v) == unsigned long long \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_problems.h b/code_examples/theory/integral_conversions_problems.h new file mode 100644 index 0000000..d301355 --- /dev/null +++ b/code_examples/theory/integral_conversions_problems.h @@ -0,0 +1,9 @@ +int x = -1; +unsigned y = 1; +long z = -1; + +auto t1 = x > y; +// x -> unsigned, t1 == true + +auto t2 = z < y; +// y -> long, t2 == true \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_same.h b/code_examples/theory/integral_conversions_same.h new file mode 100644 index 0000000..a1b1606 --- /dev/null +++ b/code_examples/theory/integral_conversions_same.h @@ -0,0 +1,5 @@ +int a = -100; +long int b = 500; + +auto v = a + b; +// v == 400, decltype(v) == long int \ No newline at end of file diff --git a/code_examples/theory/integral_promotions.h b/code_examples/theory/integral_promotions.h new file mode 100644 index 0000000..66b8844 --- /dev/null +++ b/code_examples/theory/integral_promotions.h @@ -0,0 +1,5 @@ +uint16_t a = 1; +uint16_t b = 2; + +auto v = a - b; +// v == -1, decltype(v) == int \ No newline at end of file diff --git a/code_examples/theory/references.h b/code_examples/theory/references.h new file mode 100644 index 0000000..fb6f317 --- /dev/null +++ b/code_examples/theory/references.h @@ -0,0 +1,15 @@ +void function(const int& v) {} + +long a = 0; +long long b = 0; +// Even when long and long long have the same size +static_assert(sizeof(a) == sizeof(b)); +// The two types are unrelated in the context of references +// The following two statements wouldn't compile: +// long long& c = a; +// long& d = b; + +// OK, but dangerous, implict conversion to int +// int temporary can bind to const int& +function(a); +function(b); \ No newline at end of file diff --git a/code_examples/theory/safe_compare.h b/code_examples/theory/safe_compare.h new file mode 100644 index 0000000..c21a88e --- /dev/null +++ b/code_examples/theory/safe_compare.h @@ -0,0 +1,11 @@ +int x = -1; +unsigned y = 1; +long z = -1; + +auto t1 = x > y; +auto t2 = std::cmp_greater(x,y); +// t1 == true, t2 == false + +auto t3 = z < y; +auto t4 = std::cmp_less(z,y); +// t3 == true, t4 == true \ No newline at end of file diff --git a/code_examples/theory/safe_in_range.h b/code_examples/theory/safe_in_range.h new file mode 100644 index 0000000..1a7326c --- /dev/null +++ b/code_examples/theory/safe_in_range.h @@ -0,0 +1,6 @@ +auto t1 = std::in_range(UINT_MAX); +// t1 == false +auto t2 = std::in_range(0); +// t2 == true +auto t3 = std::in_range(-1); +// t3 == false \ No newline at end of file diff --git a/code_examples/theory/safe_ssize.h b/code_examples/theory/safe_ssize.h new file mode 100644 index 0000000..b6792bb --- /dev/null +++ b/code_examples/theory/safe_ssize.h @@ -0,0 +1,8 @@ +std::vector data{1,2,3,4,5,6,7,8,9}; +// std::ssize returns ptrdiff_t, avoiding mixing +// a signed and unsigned integer in the comparison +for (ptrdiff_t i = 0; i < std::ssize(data); i++) { + std::cout << data[i] << " "; +} +std::cout << "\n"; +// prints: "1 2 3 4 5 6 7 8 9" \ No newline at end of file diff --git a/code_examples/views/elements_code.h b/code_examples/views/elements_code.h index 4c2e0f3..6aea3bf 100644 --- a/code_examples/views/elements_code.h +++ b/code_examples/views/elements_code.h @@ -3,9 +3,11 @@ std::vector> data{ }; std::vector second; -std::ranges::copy(data | std::views::elements<1>, std::back_inserter(second)); +std::ranges::copy(data | std::views::elements<1>, + std::back_inserter(second)); // second == {100, 99, 17} std::vector third; -std::ranges::copy(data | std::views::elements<2>, std::back_inserter(third)); -// third == {"Cat", "Dog", "Car"} +std::ranges::copy(data | std::views::elements<2>, + std::back_inserter(third)); +// third == {"Cat", "Dog", "Car"} \ No newline at end of file diff --git a/code_examples/views/keys_values_code.h b/code_examples/views/keys_values_code.h index 1544036..817d1cb 100644 --- a/code_examples/views/keys_values_code.h +++ b/code_examples/views/keys_values_code.h @@ -10,4 +10,4 @@ std::vector values; std::ranges::copy(std::views::values(map), std::back_inserter(values)); // values == {1.0, 1.5, 2.0, 2.5} -// in unspecified order matching order of keys +// in unspecified order matching order of keys \ No newline at end of file diff --git a/packages/color_blocks.tex b/packages/color_blocks.tex deleted file mode 100644 index 602ca3b..0000000 --- a/packages/color_blocks.tex +++ /dev/null @@ -1,28 +0,0 @@ -\usepackage[many]{tcolorbox} % for COLORED BOXES (tikz and xcolor included) - -\newtcolorbox{box-note}{ - sharpish corners, % better drop shadow - boxrule = 0pt, - toprule = 4.5pt, % top rule weight - enhanced, - breakable, - enlarge top by=5pt, - colback = white, - before skip = 0.2cm, % add extra space before the box - after skip = 0.5cm, % add extra space after the box - drop fuzzy shadow = black!35, - fontupper=\sffamily -} - -\newtcolorbox{box-nobreak}{ - sharpish corners, % better drop shadow - boxrule = 0pt, - toprule = 4.5pt, % top rule weight - enhanced, - enlarge top by=5pt, - colback = white, - before skip = 0.2cm, % add extra space before the box - after skip = 0.5cm, % add extra space after the box - drop fuzzy shadow = black!35, - fontupper=\sffamily -} \ No newline at end of file diff --git a/packages/custom_commands/CC.tex b/packages/custom_commands/CC.tex new file mode 100644 index 0000000..c3887fa --- /dev/null +++ b/packages/custom_commands/CC.tex @@ -0,0 +1,4 @@ +% Manually micro-typed "C++" + +%\newcommand{\CC}{C\nolinebreak[4]\hspace{-.1em}\raisebox{.25ex}{\small ++}\hspace{.1em}} +\newcommand{\CC}{C\nolinebreak[4]\hspace{-.1em}\sbox0{$1$}\sbox2{\small++}\raise\dimexpr(\ht0-\ht2)/2\relax\box2\hspace{.1em}} \ No newline at end of file diff --git a/packages/custom_commands/ExternalLink.tex b/packages/custom_commands/ExternalLink.tex new file mode 100644 index 0000000..f689ce4 --- /dev/null +++ b/packages/custom_commands/ExternalLink.tex @@ -0,0 +1,22 @@ +% Sourced from +% https://tex.stackexchange.com/questions/99316/symbol-for-external-links +\newcommand{\ExternalLink}{% +\tikz[x=1.2ex, y=1.2ex, baseline=-0.05ex]{% + \begin{scope}[x=1ex, y=1ex] + \clip (-0.1,-0.1) + --++ (-0, 1.2) + --++ (0.6, 0) + --++ (0, -0.6) + --++ (0.6, 0) + --++ (0, -1); + \path[draw, + line width = 0.5, + rounded corners=0.5] + (0,0) rectangle (1,1); + \end{scope} + \path[draw, line width = 0.5] (0.5, 0.5) + -- (1, 1); + \path[draw, line width = 0.5] (0.6, 1) + -- (1, 1) -- (1, 0.6); + } +} \ No newline at end of file diff --git a/packages/custom_commands/circled.tex b/packages/custom_commands/circled.tex new file mode 100644 index 0000000..2370d54 --- /dev/null +++ b/packages/custom_commands/circled.tex @@ -0,0 +1,3 @@ +\usepackage{enumitem} +\newcommand*\circled[1]{\tikz[baseline=(char.base)]{ + \node[shape=circle,draw,inner sep=1pt] (char) {#1};}} \ No newline at end of file diff --git a/packages/custom_commands/codebox.tex b/packages/custom_commands/codebox.tex new file mode 100644 index 0000000..15b30d8 --- /dev/null +++ b/packages/custom_commands/codebox.tex @@ -0,0 +1,26 @@ +\usepackage[many]{tcolorbox} % for COLORED BOXES (tikz and xcolor included) + +\newtcolorbox{codebox}[2][]{ + sharpish corners, % better drop shadow + boxrule = 0pt, + toprule = 4.5pt, % top rule weight + enhanced, + enlarge top by=5pt, + colback = white, + before skip = 0.2cm, % add extra space before the box + after skip = 0.5cm, % add extra space after the box + drop fuzzy shadow = black!35, + fontupper=\footnotesize\sffamily, + fonttitle=\footnotesize\sffamily\color{black}, + title=Open in Compiler Explorer #2, + attach boxed title to bottom right={xshift=6pt,yshift=6pt}, + boxed title style={ + enhanced, + arc=0pt, + outer arc=0pt, + boxrule = 0pt, + colback = white, + drop fuzzy shadow = black!35, + }, + #1, +} \ No newline at end of file diff --git a/packages/local.tex b/packages/custom_commands/constraints.tex similarity index 55% rename from packages/local.tex rename to packages/custom_commands/constraints.tex index 76a389e..fa30ee1 100644 --- a/packages/local.tex +++ b/packages/custom_commands/constraints.tex @@ -1,25 +1,3 @@ -\usepackage{sidenotes} % Required -\usepackage{multirow} % Required -\usepackage{diagbox} % Used for boolean algorithms. - -% Command to consistently format side-panel with C++ versions that introduced different variants of the algorithm. -\newcommand{\cppversions}[5]{ -\begin{margintable} -\footnotesize -\begin{tabular}{|m{\dimexpr.5\marginparwidth-2\tabcolsep-1pt}|m{\dimexpr.5\marginparwidth-2\tabcolsep-1pt}|} -\rowcolor{black!80} \multicolumn{2}{c}{\textcolor{white}{#1}} \\ -introduced & #2 \\ -\hline -constexpr & #3 \\ -\hline -parallel & #4 \\ -\hline -rangified & #5 \\ -\hline -\end{tabular} -\end{margintable} -} - % Command to consistently format the algorithm info table. \newcommand{\constraints}[4]{ \begin{center} diff --git a/packages/code_blocks.tex b/packages/custom_commands/cpp.tex similarity index 76% rename from packages/code_blocks.tex rename to packages/custom_commands/cpp.tex index 972f694..eda6cf9 100644 --- a/packages/code_blocks.tex +++ b/packages/custom_commands/cpp.tex @@ -10,25 +10,13 @@ \if\isOverleaf% \usepackage[newfloat=true]{minted} \else -\usepackage[newfloat=true,outputdir=build]{minted} +\usepackage[newfloat=true,cachedir=mint,outputdir=build]{minted} \fi \usepackage[]{xcolor} \usemintedstyle{xcode} - \renewcommand\theFancyVerbLine{\footnotesize\arabic{FancyVerbLine}} -\newminted{cpp}{ - linenos=true, - firstnumber=1, - tabsize=4, - obeytabs, - xleftmargin=1em, - breaklines, - fontsize=\footnotesize, - numbersep=0.75em -} - \newmintedfile[cppfile]{cpp}{ linenos=true, firstnumber=1, diff --git a/packages/custom_commands/cppversions.tex b/packages/custom_commands/cppversions.tex new file mode 100644 index 0000000..71ac5fd --- /dev/null +++ b/packages/custom_commands/cppversions.tex @@ -0,0 +1,18 @@ +% Command to consistently format side-panel with C++ versions +% that introduced different variants of the algorithm. +\newcommand{\cppversions}[5]{ +\begin{margintable} +\footnotesize +\begin{tabular}{|m{\dimexpr.5\marginparwidth-2\tabcolsep-1pt}|m{\dimexpr.5\marginparwidth-2\tabcolsep-1pt}|} +\rowcolor{black!80} \multicolumn{2}{c}{\textcolor{white}{#1}} \\ +introduced & #2 \\ +\hline +constexpr & #3 \\ +\hline +parallel & #4 \\ +\hline +rangified & #5 \\ +\hline +\end{tabular} +\end{margintable} +} \ No newline at end of file diff --git a/packages/custom_commands/version.tex b/packages/custom_commands/version.tex new file mode 100644 index 0000000..9089338 --- /dev/null +++ b/packages/custom_commands/version.tex @@ -0,0 +1 @@ +\newcommand{\version}{1.0.0} \ No newline at end of file diff --git a/packages/fonts.tex b/packages/fonts.tex index 625447d..bb09222 100644 --- a/packages/fonts.tex +++ b/packages/fonts.tex @@ -32,8 +32,4 @@ Contextuals=AlternateOff ] -\renewcommand{\familydefault}{\sfdefault} - -% Manually adjusted C++ -%\newcommand{\CC}{C\nolinebreak[4]\hspace{-.1em}\raisebox{.25ex}{\small ++}\hspace{.1em}} -\newcommand{\CC}{C\nolinebreak[4]\hspace{-.1em}\sbox0{$1$}\sbox2{\small++}\raise\dimexpr(\ht0-\ht2)/2\relax\box2\hspace{.1em}} \ No newline at end of file +\renewcommand{\familydefault}{\sfdefault} \ No newline at end of file diff --git a/packages/glossary_and_index.tex b/packages/glossary_and_index.tex deleted file mode 100644 index fd31198..0000000 --- a/packages/glossary_and_index.tex +++ /dev/null @@ -1,4 +0,0 @@ -\usepackage{glossaries} -\makeglossaries -\usepackage{imakeidx} -\makeindex \ No newline at end of file diff --git a/packages/hyperref.tex b/packages/hyperref.tex new file mode 100644 index 0000000..4b0aa75 --- /dev/null +++ b/packages/hyperref.tex @@ -0,0 +1,9 @@ +\usepackage{hyperref} +\hypersetup{ + colorlinks=true, + linkcolor=blue, + filecolor=magenta, + urlcolor=cyan, + pdftitle={Standard C++ Algorithms}, + pdfpagemode=FullScreen, + } \ No newline at end of file diff --git a/packages/packages.tex b/packages/packages.tex new file mode 100644 index 0000000..cc5556d --- /dev/null +++ b/packages/packages.tex @@ -0,0 +1,20 @@ +\usepackage{sidenotes} % Required +\usepackage{multirow} % Required +\usepackage{diagbox} % Used for boolean algorithms. +\usepackage{tikz} % Used for External link icon + +\usepackage{imakeidx} +\makeindex + +\input{packages/fonts} +\input{packages/hyperref} + +% Custom commands +\input{packages/custom_commands/ExternalLink} +\input{packages/custom_commands/cppversions} +\input{packages/custom_commands/constraints} +\input{packages/custom_commands/CC} +\input{packages/custom_commands/circled} +\input{packages/custom_commands/version} +\input{packages/custom_commands/codebox} +\input{packages/custom_commands/cpp} \ No newline at end of file From f27fd58c89ee1ca52d70089d1629239a0c85adff Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 1 Feb 2023 17:59:22 +0100 Subject: [PATCH 85/96] Update README.md Update for v1.0.0 release. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e4b1a8e..a98c87f 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v0.3.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.3.0/book_with_cover_v0.3.0.pdf) +[Latest PDF release (v1.0.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.0/book_with_cover_v1.0.0.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.3.0/book_with_cover_v0.3.0.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.0/book_with_cover_v1.0.0.pdf) ## Changelog +- `1.0.0` Content complete release with Compiler Explorer links. - `0.3.0` New chapter with ADL information and formatting cleanup. - `0.2.1` Fixed page numbering issue, small text changes. - `0.2.0` Added chapter covering C++20 ranges and views. From f136c65f94863140ed349e754fd6400a29640e48 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Thu, 2 Feb 2023 08:43:56 +0000 Subject: [PATCH 86/96] Add missing closing parenthesis. --- chapters/03_algorithms_05_divide.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index 2fc9309..e025d04 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -10,7 +10,7 @@ \subsection{\texorpdfstring{\cpp{std::lower_bound}, \cpp{std::upper_bound}}{\tex \index{\cpp{std::lower_bound}} \index{\cpp{std::upper_bound}} -The \cpp{std::lower_bound} and \cpp{std::upper_bound} algorithms offer boundary search with logarithmic complexity (for random access ranges. +The \cpp{std::lower_bound} and \cpp{std::upper_bound} algorithms offer boundary search with logarithmic complexity (for random access ranges). \cppversions{\texttt{lower\_bound}}{\CC98}{\CC20}{N/A}{\CC20} \cppversions{\texttt{upper\_bound}}{\CC98}{\CC20}{N/A}{\CC20} From 0f7d39812a8a02fe86b9b32e7fffc0ffc31b01f4 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Thu, 2 Feb 2023 11:48:00 +0000 Subject: [PATCH 87/96] Adjust O(n*logn) formatting. --- chapters/03_algorithms_03_sorting.tex | 6 +++--- chapters/03_algorithms_05_divide.tex | 2 +- chapters/03_algorithms_15_heap.tex | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chapters/03_algorithms_03_sorting.tex b/chapters/03_algorithms_03_sorting.tex index 4351d54..fd25854 100644 --- a/chapters/03_algorithms_03_sorting.tex +++ b/chapters/03_algorithms_03_sorting.tex @@ -79,12 +79,12 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\textt \subsection{\texorpdfstring{\cpp{std::sort}}{\texttt{std::sort}}} \index{\cpp{std::sort}} -The \cpp{std::sort} algorithm is the canonical $O(n*logn)$ sort (typically implemented as intro-sort). +The \cpp{std::sort} algorithm is the canonical $O(n\log n)$ sort (typically implemented as intro-sort). \cppversions{\texttt{sort}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\cpp{random_access_range}}{\cpp{random_access_range}}{\cpp{operator<}}{\cpp{strict_weak_ordering}} -Due to the $O(n*logn)$ complexity guarantee, \cpp{std::sort} only operates on \cpp{random_access} ranges. Notably, \cpp{std::list} offers a method with an approximate $n*logn$ complexity. +Due to the $O(n\log n)$ complexity guarantee, \cpp{std::sort} only operates on \cpp{random_access} ranges. Notably, \cpp{std::list} offers a method with an approximate $n\log n$ complexity. \begin{codebox}[]{\href{https://compiler-explorer.com/z/vef61TWj9}{\ExternalLink}} \footnotesize Basic example of using \cpp{std::sort} and \cpp{std::list::sort}. @@ -110,7 +110,7 @@ \subsection{\texorpdfstring{\cpp{std::stable_sort}}{\texttt{std::stable\_sort}}} \cppversions{\texttt{stable\_sort}}{\CC98}{N/A}{\CC17}{\CC20} \constraints{\cpp{random_access_range}}{}{\cpp{operator<}}{\cpp{strict_weak_ordering}} -If additional memory is available, stable\_sort remains $O(n*logn)$. However, if it fails to allocate, it will degrade to an $O(n*logn*logn)$ algorithm. +If additional memory is available, stable\_sort remains $O(n\log n)$. However, if it fails to allocate, it will degrade to an $O(n\log n\log n)$ algorithm. \begin{codebox}[]{\href{https://compiler-explorer.com/z/TKx8qP8bK}{\ExternalLink}} \footnotesize Example of re-sorting a range using \cpp{std::stable_sort}, resulting in a guaranteed order of elements. diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index e025d04..98e8c51 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -30,7 +30,7 @@ \subsection{\texorpdfstring{\cpp{std::lower_bound}, \cpp{std::upper_bound}}{\tex \cppfile{code_examples/algorithms/lower_bound_code.h} \end{codebox} -While the algorithms will operate on any \cpp{forward_range}, the logarithmic divide and conquer behaviour is only available for \cpp{random_access_range}. Data structures like \cpp{std::set}, \cpp{std::multiset}, \cpp{std::map} and \cpp{std::multimap} offer their $O(logn)$ implementations of lower and upper bound as methods. +While the algorithms will operate on any \cpp{forward_range}, the logarithmic divide and conquer behaviour is only available for \cpp{random_access_range}. Data structures like \cpp{std::set}, \cpp{std::multiset}, \cpp{std::map} and \cpp{std::multimap} offer their $O(\log n)$ implementations of lower and upper bound as methods. \begin{codebox}[]{\href{https://compiler-explorer.com/z/o9Wdvzno9}{\ExternalLink}} \footnotesize Example of using \cpp{lower_bound} and \cpp{upper_bound} methods on a \cpp{std::multiset}. diff --git a/chapters/03_algorithms_15_heap.tex b/chapters/03_algorithms_15_heap.tex index 89526b4..e2db31d 100644 --- a/chapters/03_algorithms_15_heap.tex +++ b/chapters/03_algorithms_15_heap.tex @@ -39,7 +39,7 @@ \subsection{\texorpdfstring{\cpp{std::make_heap}, \cpp{std::push_heap}, \cpp{std \subsection{\texorpdfstring{\cpp{std::sort_heap}}{\texttt{std::sort\_heap}}} \index{\cpp{std::sort_heap}} -The \cpp{std::sort_heap} will reorder the elements in a heap into a sorted order. Note that this is the same as repeatedly calling \cpp{std::pop_heap}; hence the algorithm has $O(n*logn)$ complexity. +The \cpp{std::sort_heap} will reorder the elements in a heap into a sorted order. Note that this is the same as repeatedly calling \cpp{std::pop_heap}; hence the algorithm has $O(n\log n)$ complexity. \cppversions{\texttt{sort\_heap}}{\CC98}{\CC20}{N/A}{\CC20} \constraints{\texttt{random\_access\_range}}{}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} From 4cb5bfdf5c33576c03b61fd163cb91aff098c4fb Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 18:34:49 +0000 Subject: [PATCH 88/96] Replace non-standard ssize_t. --- chapters/02_introduction.tex | 2 +- chapters/03_algorithms_01_foreach.tex | 2 +- code_examples/algorithms/for_each_n.cpp | 2 +- code_examples/algorithms/for_each_n_code.h | 2 +- code_examples/introduction/categories_code.h | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 50599c2..9a29019 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -82,7 +82,7 @@ \subsection{Iterator categories} \textit{arrays, e.g. std::vector}\index{\cpp{std::vector}} \end{description} -\begin{codebox}[]{\href{https://godbolt.org/z/dcaMEdnoM}{\ExternalLink}} +\begin{codebox}[]{\href{https://godbolt.org/z/aWT1Wajva}{\ExternalLink}} \footnotesize Example demonstrating the difference between a random access iterator provided by \cpp{std::vector} and a bidirectional iterator provided by \cpp{std::list}. \tcblower \cppfile{code_examples/introduction/categories_code.h} diff --git a/chapters/03_algorithms_01_foreach.tex b/chapters/03_algorithms_01_foreach.tex index 0c7e8b3..2ceed90 100644 --- a/chapters/03_algorithms_01_foreach.tex +++ b/chapters/03_algorithms_01_foreach.tex @@ -97,7 +97,7 @@ \subsection{\texorpdfstring{\cpp{std::for_each_n}}{\texttt{std::for\_each\_n}}} \raggedbottom -\begin{codebox}[]{\href{https://compiler-explorer.com/z/1hfje76Yq}{\ExternalLink}} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/o8rEzc6q3}{\ExternalLink}} \footnotesize Example demonstrating multiple uses of \cpp{std::for_each_n}. \tcblower \cppfile{code_examples/algorithms/for_each_n_code.h} diff --git a/code_examples/algorithms/for_each_n.cpp b/code_examples/algorithms/for_each_n.cpp index 7a6e8ad..eab3cce 100644 --- a/code_examples/algorithms/for_each_n.cpp +++ b/code_examples/algorithms/for_each_n.cpp @@ -5,7 +5,7 @@ #include inline constexpr const size_t MAIN_SEATS = 2; -inline constexpr const ssize_t PAGE_SIZE = 2; +inline constexpr const size_t PAGE_SIZE = 2; struct Player { std::string display_name; diff --git a/code_examples/algorithms/for_each_n_code.h b/code_examples/algorithms/for_each_n_code.h index b504025..14922b4 100644 --- a/code_examples/algorithms/for_each_n_code.h +++ b/code_examples/algorithms/for_each_n_code.h @@ -9,7 +9,7 @@ std::for_each_n(std::execution::par_unseq, auto it = final_ranking.begin(); uint32_t page = 0; while (it != final_ranking.end()) { - ssize_t cnt = std::min(PAGE_SIZE, final_ranking.end() - it); + size_t cnt = std::min(PAGE_SIZE, size_t(final_ranking.end() - it)); std::for_each_n(it, cnt, [page](const Player& p) { store_final_score(page, p.display_name, p.score); }); diff --git a/code_examples/introduction/categories_code.h b/code_examples/introduction/categories_code.h index f1cd3cc..33c59e1 100644 --- a/code_examples/introduction/categories_code.h +++ b/code_examples/introduction/categories_code.h @@ -3,7 +3,7 @@ auto it1 = arr.begin(); it1 += 5; // OK, std::vector provides random access iterator ++it1; // OK, all iterators provide advance operation -ssize_t dst1 = it1 - arr.begin(); // OK, random access iterator +ptrdiff_t dst1 = it1 - arr.begin(); // OK, random access iterator // dst1 == 6 std::list lst = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; @@ -13,5 +13,5 @@ std::advance(it2, 5); // OK, linear advance by 5 steps ++it2; // OK, all iterators provide advance operation // it2 - lst.begin(); Would not compile -ssize_t dst2 = std::distance(lst.begin(), it2); // OK, linear calc. +ptrdiff_t dst2 = std::distance(lst.begin(), it2); // OK, linear calc. // dst2 == 6 From 6cce920bc6de220bce3d5850f196f6226d073b73 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 19:10:07 +0000 Subject: [PATCH 89/96] Custom margin for title and copyright page. --- chapters/00_title_page.tex | 4 +++- packages/packages.tex | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index 402505f..b6a7b15 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -1,3 +1,4 @@ +\newgeometry{margin=5cm} \begin{titlepage} \centering \vspace{2cm} @@ -41,4 +42,5 @@ \endgroup \clearpage -\pagestyle{plain} \ No newline at end of file +\pagestyle{plain} +\restoregeometry \ No newline at end of file diff --git a/packages/packages.tex b/packages/packages.tex index cc5556d..c7b4f71 100644 --- a/packages/packages.tex +++ b/packages/packages.tex @@ -6,6 +6,8 @@ \usepackage{imakeidx} \makeindex +\usepackage{geometry} + \input{packages/fonts} \input{packages/hyperref} From 4096f285a5489de9d3b31a73db69e88dc7accd9b Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 19:10:37 +0000 Subject: [PATCH 90/96] Update version for new release. --- packages/custom_commands/version.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/custom_commands/version.tex b/packages/custom_commands/version.tex index 9089338..e0ac109 100644 --- a/packages/custom_commands/version.tex +++ b/packages/custom_commands/version.tex @@ -1 +1 @@ -\newcommand{\version}{1.0.0} \ No newline at end of file +\newcommand{\version}{1.0.1} \ No newline at end of file From 406b52c7a0cee2a81a86dd88a899b1491f1380cd Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 19:14:50 +0000 Subject: [PATCH 91/96] Update changelog. --- chapters/01_preface.tex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chapters/01_preface.tex b/chapters/01_preface.tex index 52d5d3f..c3f4536 100644 --- a/chapters/01_preface.tex +++ b/chapters/01_preface.tex @@ -41,5 +41,6 @@ \section*{Book status} \subsection*{Changelog} \begin{enumerate} - \item[1.0] First complete release + \item[1.0.1] Small (mostly) formatting changes. + \item[1.0.0] First complete release. \end{enumerate} \ No newline at end of file From b13443990832d96f73dcd54a7394217004f2aca7 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 19:38:13 +0000 Subject: [PATCH 92/96] Revert "Custom margin for title and copyright page." This reverts commit 6cce920bc6de220bce3d5850f196f6226d073b73. --- chapters/00_title_page.tex | 4 +--- packages/packages.tex | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index b6a7b15..402505f 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -1,4 +1,3 @@ -\newgeometry{margin=5cm} \begin{titlepage} \centering \vspace{2cm} @@ -42,5 +41,4 @@ \endgroup \clearpage -\pagestyle{plain} -\restoregeometry \ No newline at end of file +\pagestyle{plain} \ No newline at end of file diff --git a/packages/packages.tex b/packages/packages.tex index c7b4f71..cc5556d 100644 --- a/packages/packages.tex +++ b/packages/packages.tex @@ -6,8 +6,6 @@ \usepackage{imakeidx} \makeindex -\usepackage{geometry} - \input{packages/fonts} \input{packages/hyperref} From 299a8a57228a04f3bcbeab8d812387c8c0f57870 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 4 Feb 2023 09:43:18 +0100 Subject: [PATCH 93/96] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a98c87f..21cebfc 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v1.0.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.0/book_with_cover_v1.0.0.pdf) +[Latest PDF release (v1.0.1)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.1/a_complete_guide_to_standard_cpp_algorithms_v1_0_1.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.0/book_with_cover_v1.0.0.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.1/a_complete_guide_to_standard_cpp_algorithms_v1_0_1.pdf) ## Changelog +- `1.0.1` Small (mostly) formatting fixes. - `1.0.0` Content complete release with Compiler Explorer links. - `0.3.0` New chapter with ADL information and formatting cleanup. - `0.2.1` Fixed page numbering issue, small text changes. From fedf78f01606b3c0a4c88e9590b12d6f7c91a922 Mon Sep 17 00:00:00 2001 From: wxinix Date: Wed, 5 Apr 2023 08:48:14 -0400 Subject: [PATCH 94/96] Update READEME with additional info about alternative approach of building from sources using TexStudio. --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 21cebfc..c432bc0 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,16 @@ The resulting PDF will be in the build folder. Most code files have wrapping main files that exercise both the build and also contain `assert` expressions that verify correctness of the code. The `verify.sh` shell script will build all (except for a few that don't compile with GCC 11) examples, and then run each of them to validate all asserts. + +## Alternative approach of building from sources + +If you're building from sources inside a virtual machine and the VSCode and Docker approach doesn't work for you, [TexStudio](https://www.texstudio.org/) provides an alternative solution for Windows users. Follow the steps below: + +- Install [Anaconda](https://www.anaconda.com/) and add `C:\Users\YourUserName\anaconda3\Scripts` to system path. Replace `YourUserName` with your actual user name. +- Install [TexStudio](https://www.texstudio.org/). +- Go to Texstudio menu "Options -> Configure Texstudio -> Commands -> XeLaTex", and enter the following command: `xelatex.exe -synctex=1 -interaction=nonstopmode -shell-escape -aux-directory=build -output-directory=build %.tex`. This command sets both the aux and output directories to ".\build". +- Go to Texstudio menu "Options -> Configure Texstudio -> Build -> Meta Commands -> Default Compiler", and enter `txs:///xelatex`. This switches the default compiler from `pdflatex` to `xelatex`. Then, enable the checkbox "Show Advanced Options". +- For Texstudio menu "Options -> Configure Texstudio -> Build -> Build Options -> PDF File", enter `build`. This tells the pdf previewer to look for the generated pdf file in the ".\build" directory. +- Finally, go to "Texstudio menu Options -> Configure Texstudio -> Commands -> Makeindex", and enter the following command: `makeindex.exe build%.idx` + +These steps will enable you to build from sources inside a virtual machine using TexStudio on Windows. From 68fe51a2c2770f5c767594c85c3b2de322f0bd6e Mon Sep 17 00:00:00 2001 From: wxinix Date: Wed, 5 Apr 2023 10:51:23 -0400 Subject: [PATCH 95/96] Update README to better clarify the alternative solution for building from sources using Texstudio inside a virtual machine on Windows. --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c432bc0..e308e47 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,15 @@ The `verify.sh` shell script will build all (except for a few that don't compile ## Alternative approach of building from sources -If you're building from sources inside a virtual machine and the VSCode and Docker approach doesn't work for you, [TexStudio](https://www.texstudio.org/) provides an alternative solution for Windows users. Follow the steps below: +This section was provided by [@wxinix](https://github.com/wxinix). Please direct any questions to him. + +[Texstudio](https://www.texstudio.org/) offers an alternative solution for Windows users who are building from sources within a virtual machine and are unable to use the VSCode and Docker approach. Follow the steps below: - Install [Anaconda](https://www.anaconda.com/) and add `C:\Users\YourUserName\anaconda3\Scripts` to system path. Replace `YourUserName` with your actual user name. -- Install [TexStudio](https://www.texstudio.org/). +- Install [Texstudio](https://www.texstudio.org/). - Go to Texstudio menu "Options -> Configure Texstudio -> Commands -> XeLaTex", and enter the following command: `xelatex.exe -synctex=1 -interaction=nonstopmode -shell-escape -aux-directory=build -output-directory=build %.tex`. This command sets both the aux and output directories to ".\build". - Go to Texstudio menu "Options -> Configure Texstudio -> Build -> Meta Commands -> Default Compiler", and enter `txs:///xelatex`. This switches the default compiler from `pdflatex` to `xelatex`. Then, enable the checkbox "Show Advanced Options". - For Texstudio menu "Options -> Configure Texstudio -> Build -> Build Options -> PDF File", enter `build`. This tells the pdf previewer to look for the generated pdf file in the ".\build" directory. - Finally, go to "Texstudio menu Options -> Configure Texstudio -> Commands -> Makeindex", and enter the following command: `makeindex.exe build%.idx` -These steps will enable you to build from sources inside a virtual machine using TexStudio on Windows. +These steps will enable you to build from sources inside a virtual machine using TexStudio on Windows. Ask @wxinix if you have any questions. From f81c7c49f4314a88924472b9569e4a981d60f44d Mon Sep 17 00:00:00 2001 From: wxinix Date: Wed, 5 Apr 2023 18:32:02 -0400 Subject: [PATCH 96/96] Update README with typo fixes to better explain the alternative solution for building from sources using TeXstudio in a virtual machine environment. --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e308e47..e9d3208 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ The `verify.sh` shell script will build all (except for a few that don't compile This section was provided by [@wxinix](https://github.com/wxinix). Please direct any questions to him. -[Texstudio](https://www.texstudio.org/) offers an alternative solution for Windows users who are building from sources within a virtual machine and are unable to use the VSCode and Docker approach. Follow the steps below: +[TeXstudio](https://www.texstudio.org/) offers an alternative solution for those who are building from sources in a virtual machine environment and are unable to use the VSCode and Docker approach. Follow the steps below (assuming the Guest OS of the virtual machine is Windows): - Install [Anaconda](https://www.anaconda.com/) and add `C:\Users\YourUserName\anaconda3\Scripts` to system path. Replace `YourUserName` with your actual user name. -- Install [Texstudio](https://www.texstudio.org/). -- Go to Texstudio menu "Options -> Configure Texstudio -> Commands -> XeLaTex", and enter the following command: `xelatex.exe -synctex=1 -interaction=nonstopmode -shell-escape -aux-directory=build -output-directory=build %.tex`. This command sets both the aux and output directories to ".\build". -- Go to Texstudio menu "Options -> Configure Texstudio -> Build -> Meta Commands -> Default Compiler", and enter `txs:///xelatex`. This switches the default compiler from `pdflatex` to `xelatex`. Then, enable the checkbox "Show Advanced Options". -- For Texstudio menu "Options -> Configure Texstudio -> Build -> Build Options -> PDF File", enter `build`. This tells the pdf previewer to look for the generated pdf file in the ".\build" directory. -- Finally, go to "Texstudio menu Options -> Configure Texstudio -> Commands -> Makeindex", and enter the following command: `makeindex.exe build%.idx` +- Install [TeXstudio](https://www.texstudio.org/). +- Go to TeXstudio menu "Options -> Configure TeXstudio -> Commands -> XeLaTex", and enter the following command: `xelatex.exe -synctex=1 -interaction=nonstopmode -shell-escape -aux-directory=build -output-directory=build %.tex`. This command sets both the aux and output directories to ".\build". +- Go to TeXstudio menu "Options -> Configure TeXstudio -> Build -> Meta Commands -> Default Compiler", and enter `txs:///xelatex`. This switches the default compiler from `pdflatex` to `xelatex`. Then, enable the checkbox "Show Advanced Options". +- For TeXstudio menu "Options -> Configure TeXstudio -> Build -> Build Options -> PDF File", enter `build`. This tells the pdf previewer to look for the generated pdf file in the ".\build" directory. +- Finally, go to "TeXstudio menu Options -> Configure TeXstudio -> Commands -> Makeindex", and enter the following command: `makeindex.exe build%.idx` -These steps will enable you to build from sources inside a virtual machine using TexStudio on Windows. Ask @wxinix if you have any questions. +These steps will enable you to build from sources inside a virtual machine using TeXstudio. Keep in mind that the VSCode and Docker approach might not work in a virtual machine environment, because it requires extra set-up for [nested virtulization](https://stackoverflow.com/questions/39720254/can-i-run-docker-in-a-virtual-machine). Ask @wxinix if you have any questions.