This set of slides introduces the reader to a subset of the C++ Standard Library called the Standard Template Library (STL). The STL provides a collection of parameterized containers and algorithms, and it is the most successful example of an approach to programming called generic programming. In this presentation, we aim at studying the ideals and concepts of the STL by re-implementing small parts of the library. Specifically, we first show how we can discover requirements on types in order to devise generic algorithms. Then, we focus on how to make algorithms independent of containers through the pivotal abstraction of iterators. To this end, we replicate the standard algorithm for finding the minimum in a sequence (min_element), which we subsequently match with a custom forward iterator over intrusive linked lists of integers. Finally, we see how function objects can be used to customize containers and algorithms alike. This allows us to deepen our understanding of ordering relations, and, in particular, to introduce the concept of strict weak orderings.
4. The Standard Template Library
The C++ Standard Library includes a set of algorithms and data
structures that were originally part of a third-party library, called
the Standard Template Library (STL)
5. The Standard Template
Library
The Standard Template Library was
invented by Alexander Stepanov in 1994
circa, and later included in the C++
Standard Library
6. The Standard Template
Library
The STL is the outcome of two decades of
Stepanov's eο¬ort in delinea8ng the
fundamental principles of programming
8. An approach to computa-on
"The STL is unlike any other library you have seen before."
(S. T. Lavavej)
9. An approach to computa-on
"People look at STL and think it's a nice container and algorithm library.
It is not, it is a diο¬erent way of thinking about code."
(S. Parent)
11. Storing and manipula/ng objects
Consider the problem of storing objects in containers and wri6ng
algorithms to manipulate such objects
12. Storing and manipula/ng objects
We want to be able to store objects of a variety of types in a
variety of containers and to apply a variety of algorithms
13. Storing and manipula/ng objects
Furthermore, we want the use of these objects, containers, and
algorithms to be:
β’ Sta%cally type safe
β’ As fast possible
β’ As compact as possible
β’ Not verbose and readable
14. Storing and manipula/ng objects
"Achieving all of this simultaneously isn't easy. In fact, I spent about 10
years unsuccessfully looking for a solu>on to this puzzle."
(B. Stroustrup)
15. List inser)on
In order to appreciate this, let us consider the problem of inser5ng
an arbitrary long sequence of progressive numbers into a list
16. List inser)on
We will see how to solve this problem using STL and Glib1
1
Glib is a general-purpose C library associated with the GNOME desktop environment. For more informaBon about
GLib, see hEps://developer.gnome.org/glib/stable/
18. Glib: inser+on
GList* list = NULL;
int* data_ptr;
for (int i = 0; i < 10; i++) {
data_ptr = g_new(int, 1);
*data_ptr = i;
list = g_list_append(list, (void*) data_ptr);
}
19. The NΒ·M problem
Before the STL, all the available general computa8on libraries
suο¬ered from the NΒ·M problem
20. The NΒ·M problem
If you have N containers and M algorithms, you need NΒ·M diο¬erent
implementa;ons in order to provide each algorithm for each
possible container
21. Glib: ο¬nding in a doubly-linked list
GList* list = ...;
GList* tmp;
int* data_ptr;
data_ptr = g_new(int, 1);
*data_ptr = 7;
tmp = g_list_find(list, (void*) data_ptr);
22. Glib: ο¬nding in a singly-linked list
GSList* list = ...;
GSList* tmp;
int* data_ptr;
data_ptr = g_new(int, 1);
*data_ptr = 7;
tmp = g_slist_find(list, (void*) data_ptr);
23. The STL solu+on
The STL solu+on is based on parameterizing containers with their
element types and on completely separa-ng the algorithms from
the containers
24. STL: ο¬nding in a doubly-linked list
std::list<int> l = {1, 10, 8, 7};
auto p = std::find(l.begin(), l.end(), 7);
25. STL: ο¬nding in a singly-linked list
std::forward_list<int> l = {1, 10, 8, 7};
auto p = std::find(l.begin(), l.end(), 7);
26. The STL is extensible
Since containers and algorithms are independent, introducing a
new container does not require reimplemen6ng all the available
algorithms
27. The STL is extensible
All available algorithms will simply work with the new container
33. swap alterna(ves
At ο¬rst, we wrote several alterna*ves of the swap func0on, each
of which tailored for a speciο¬c built-in type
void swap(int& x, int& y) {
int tmp = x;
x = y;
y = tmp;
}
34. swap alterna(ves
At ο¬rst, we wrote several alterna*ves of the swap func0on, each
of which tailored for a speciο¬c built-in type
void swap(double& x, double& y) {
double tmp = x;
x = y;
y = tmp;
}
35. swap alterna(ves
At ο¬rst, we wrote several alterna*ves of the swap func0on, each
of which tailored for a speciο¬c built-in type
void swap(char& x, char& y) {
char tmp = x;
x = y;
y = tmp;
}
36. swap for built-in types
Later, we no+ced that we can devise a uniform implementa.on of
swap for any built-in type T
// T is a built-in type
void swap(T& x, T& y) {
T tmp = x;
x = y;
y = tmp;
}
37. swap for semi-regular types
Ul#mately, we observed that the very same implementa#on is s#ll
valid for any copy-construc+ble and assignable type
// T is copy-constructible and assignable
void swap(T& x, T& y) {
T tmp = x;
x = y;
y = tmp;
}
38. swap for semi-regular types
That is, the same implementa.on is valid for any semi-regular type
// T is semi-regular
void swap(T& x, T& y) {
T tmp = x;
x = y;
y = tmp;
}
41. Language limita,ons
This is because the compiler requires func4on parameters to be
exis%ng types
// T is semi-regular
void swap(T& x, T& y) {
T tmp = x;
x = y;
y = tmp;
}
42. Language limita,ons
Apparently, the language is limi&ng us in harnessing the underlying
regularity we were able to no5ce
// T is semi-regular
void swap(T& x, T& y) {
T tmp = x;
x = y;
y = tmp;
}
45. swap as a func(on template
This allows us to deο¬ne at once all the alterna1ve versions of swap
function template: swap(T&, T&)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β β’ swap(double&, double&) β
β β
β β
β β’ swap(int&, int&) β
β β
β β
β β’ swap(char&, char&) β
β β
β β’ swap(float&, float&) β
β β
β β
β β
β β’ swap(vector_int&, vector_int&) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
46. swap as a func(on template
The deο¬ni)on of swap as a func)on template is as follows:
template <typename T> // T is semi-regular
void swap(T& x, T& y) {
T tmp = x;
x = y;
y = tmp;
}
47. Template instan,a,on
The func. template generates as many actual func1ons as needed
T = double β swap(double&, double&)
52. std::swap
In fact, we derived the deο¬ni1on of std::swap as of C++03
template <typename T> // T is semi-regular
void swap(T& x, T& y) {
T tmp = x;
x = y;
y = tmp;
}
54. The min_element algorithm
In a previous lecture, we devised an algorithm for ο¬nding the
minimum element of an integer array
55. The min_element algorithm
int* min_element(int* first, int* last) {
if (first == last) return last;
int* min = first;
++first;
while (first != last) {
if (*first < *min) min = first;
++first;
}
return min;
}
56. The min_element algorithm
It is immediate to see that the procedure does not depend on the
elements being integers
int* min_element(int* first, int* last) { ... }
57. The min_element algorithm
It is immediate to see that the procedure does not depend on the
elements being integers
double* min_element(double* first, double* last) { ... }
58. The min_element algorithm
It is immediate to see that the procedure does not depend on the
elements being integers
char* min_element(char* first, char* last) { ... }
59. The min_element algorithm
Hence, we can write min_element as a func%on template
template <typename I>
I min_element(I first, I last) { ... }
60. min_element as a func(on template
template <typename I>
I min_element(I first, I last) {
if (first == last) return last;
I min = first;
++first;
while (first != last) {
if (*first < *min) min = first;
++first;
}
return min;
}
61. Element type independence
We can now invoke min_element regardless of the element type
template <typename I>
I min_element(I first, I last) { ... }
62. Using min_element
template <typename I>
I min_element(I first, I last) { ... }
int main() {
double ds[] = {7.1, 2.3, 5.6, 9.0};
char cs[] = {'a', 'b', 'c'};
double* d = min_element(ds, ds + 4);
char* c = min_element(cs, cs + 3);
}
63. Using min_element
I is derived to be double* when passing an array of m elements of
type double
template <typename I>
I min_element(I first, I last) { ... }
64. Using min_element
I is derived to be char* when passing an array of m elements of
type char
template <typename I>
I min_element(I first, I last) { ... }
66. Finding the minimum
The algorithm for ο¬nding the minimum is independent of the way
we store elements in memory
βββββ¬ββββ¬ββββ¬ββββ¬ β β
xs: β 1 β 7 β13 β 5 β
βββββ΄ββββ΄ββββ΄ββββ΄ β β
βββββ βββββ βββββ βββββ
xs: β 1 ββββ€ 7 ββββ€13 ββββ€ 5 ββββΆ *
βββββ βββββ βββββ βββββ
67. Finding the minimum
Because of this, we should be able to apply min_element on
linked lists as well
βββββ¬ββββ¬ββββ¬ββββ¬ β β
xs: β 1 β 7 β13 β 5 β
βββββ΄ββββ΄ββββ΄ββββ΄ β β
βββββ βββββ βββββ βββββ
xs: β 1 ββββ€ 7 ββββ€13 ββββ€ 5 ββββΆ *
βββββ βββββ βββββ βββββ
68. Requirements on I
For min_element to work, we need some proper/es to hold for I
template <typename I>
I min_element(I first, I last) {
if (first == last) return last;
I min = first;
++first;
while (first != last) {
if (*first < *min) min = first;
++first;
}
return min;
}
69. Requirements on I
Namely, we require I to be:
β’ Equality & inequality comparable
β’ Copy construc6ble & assignable
β’ Incrementable
70. Passing a pointer
For arrays, I is derived to be a pointer
template <typename I>
I min_element(I first, I last) { ... }
71. Passing a pointer
Since pointers sa-sfy the requirements we iden-ο¬ed, the algorithm
works
template <typename I>
I min_element(I first, I last) { ... }
73. Linked lists
Therefore, we need to subs0tute pointers with a user-deο¬ned type
that s0ll meets the requirements for I
template <typename I>
I min_element(I first, I last) { ... }
74. Iterators
We refer to such user-deο¬ned types as iterators
class list_iterator {
...
};
75. Linked list iterator
Namely, we will write a class named list_iterator to be used
in conjunc5on with the node class
class list_iterator {
...
};
83. Dereference constness
Note that operator* is always const, even for non-const
iterators
int& operator*() const { return curr->value; }
84. Dereference constness
Constness only means that the iterator cannot be later modiο¬ed so
as to point to a diο¬erent posi5on in the data structure
list_iterator const it1 = ...;
it1 = it2; // it1 cannot be re-assigned so as to point to a different location
85. Dereference constness
Nevertheless, it is perfectly valid to modify the data pointed to by a
constant iterator
list_iterator const it1 = ...;
*it1 = 10;
86. Dereference constness
Recall that we are trying to mimic pointers, and this is perfectly
aligned with the way pointers behave:
int a = 10, b = 7;
int* const p = &a;
p = &b; // error: p is a constant pointer
*p = 5; // legit: a is now 5
93. min_element
void print_min(node* head) {
list_iterator first(head);
list_iterator last;
list_iterator m = min_element(first, last);
if (m != last)
std::cout << "the min is " << *m;
}
94. Algorithms vs. containers
Note that min_element is completely unaware of the existence
of any container
template <typename I>
I min_element(I first, I last) { ... }
95. Algorithms vs. containers
As a ma&er of fact, min_element works as long as we provide a
type that meets the requirements for I
template <typename I>
I min_element(I first, I last) { ... }
96. Algorithms vs. containers
"The reason that data structures and algorithms can work together
seamlessly is... that they do not know anything about each other."
(A. Stepanov)
97. Iterators delimit sequences
An iterator allows abstrac/ng an arbitrary container as a sequence
of elements
βββββ¬ββββ¬ββββ¬ β β
β 7 β12 β 3 β
βββββ΄ββββ΄ββββ΄ ββ²β
β
β
β
past-the-end
element
98. Iterators delimit sequences
This is true regardless of the fact that elements may in fact not be
con4guously allocated in memory (as with linked lists)
3rd past-the-end 1st 2nd
elem. elem. elem. elem.
βββββββΆ ββββββββΆ ββββββββΆ ββββββββΆ
βββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ¬ββββ
... β β β///β///β β β///β///β β β β β///β///β β β///β///β ...
βββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ΄ββββ
βββΆ
1 byte
104. Requirements on I
Therefore, we can now reο¬ne the requirements on I
template <typename I>
I min_element(I first, I last) { ... }
105. Requirements on I
Namely, we require I to be a forward iterator
template <typename I>
I min_element(I first, I last) { ... }
106. Requirements on I
Namely, we require I to be a forward iterator
template <typename I> // I is a forward iterator
I min_element(I first, I last) { ... }
107. Requirements on I
Moreover, the type resul/ng from dereferencing the forward
iterator (i.e., its value type) should model a totally ordered type
// I is a forward iterator
// I's value type is a totally ordered type
template <typename I>
I min_element(I first, I last) { ... }
108. Top-down vs. bo-om-up
No#ce that we could not iden#fy the requirements without looking
at the implementa)on
// I is a forward iterator
// I's value type is a totally ordered type
template <typename I>
I min_element(I first, I last) { ... }
109. Top-down vs. bo-om-up
This is to say that β some-mes β we cannot determine the
interface in a top-down fashion
// I is a forward iterator
// I's value type is a totally ordered type
template <typename I>
I min_element(I first, I last) { ... }
110. Top-down vs. bo-om-up
Some%mes, we need to look at the implementa%on to determine
the interface
// I is a forward iterator
// I's value type is a totally ordered type
template <typename I>
I min_element(I first, I last) { ... }
112. vector_int
During previous classes, we were able to incrementally write a
simpliο¬ed version of std::vector called vector_int
class vector_int {
...
};
115. Algorithms expect iterators
As we just saw, algorithms that operate on a sequence expects a
pair of iterators, rather than the container per se
// I is a forward iterator
// I's value type is a totally ordered type
template <typename I>
I min_element(I first, I last) { ... }
117. begin() and end()
Namely, vector_int should provide two member func8ons
named begin() and end()2
2
And possible constant variants cbegin() and cend()
118. begin() and end()
begin() and end() should return an iterator to the ο¬rst and to
the past-the-end element, respec2vely
βββββ¬ββββ¬ββββ¬ β β
β 7 β12 β 3 β
βββββ΄ββββ΄ββββ΄ ββ²β
β
β
β
past-the-end
element
119. begin() and end()
Let us add begin() and end() to vector_int
class vector_int {
public:
...
??? begin() { ??? }
??? end() { ??? }
private:
std::size_t sz;
int* elem;
};
120. begin() and end()
Let us add begin() and end() to vector_int
class vector_int {
public:
...
int* begin() { return elem; }
int* end() { return elem + sz; }
private:
std::size_t sz;
int* elem;
};
122. vector_int and min_element
template <typename I>
I min_element(I first, I last) { ... }
void print_min(vector_int v) {
int* first = v.begin();
int* last = v.end();
int* m = min_element(first, last);
if (m != last)
std::cout << "the min is " << *m;
}
123. vector_int and min_element
Although the code works, we s1ll have a issue
void print_min(vector_int v) {
int* first = v.begin();
int* last = v.end();
int* m = min_element(first, last);
if (m != last)
std::cout << "the min is " << *m;
}
124. vector_int and min_element
More precisely, we are revealing too much about the type of the
iterator
int* first = v.begin();
int* last = v.end();
125. vector_int and min_element
Users of vector_int should not be concerned with the fact that
iterators are either pointers or user-deο¬ned types
int* first = v.begin();
int* last = v.end();
126. Member type aliases
Thus, we will hide the actual type of the iterator through the use of
member type aliases (or simply member types)
129. Member type aliases
class X {
public:
using integer = int;
};
int main() {
// the same as: int a = 5;
X::integer a = 5;
}
130. Iterator member types
Type aliases for iterators and const iterators are required to be
called, resp., iterator and const_iterator
class vector_int {
public
using iterator = ???
using const_iterator = ???
...
};
131. Iterator member types
Type aliases for iterators and const iterators are required to be
called, resp., iterator and const_iterator
class vector_int {
public
using iterator = int*;
using const_iterator = int const*;
...
};
132. Iterator member types
We consequently change the return type of begin() and end()
class vector_int {
public
using iterator = int*;
using const_iterator = int const*;
...
};
134. Random access iterator
No#ce that vector_int::iterator is a random access iterator
vector_int::iterator first = v.begin();
first = first + 2; // random access
135. std::vector's iterator type
Similarly, given a value of type std::vector<T>, the related
iterator type is std::vector<T>::iterator
std::vector<int> v = {7, 10, 5, 8};
std::vector<int>::iterator first = v.begin();
std::vector<int>::iterator last = v.end();
136. vector_double
It is immediate to see that the if we had to write vector_double,
its implementa3on would not diο¬er from that of vector_int
class vector_double {
public:
...
private:
std::size_t sz;
double* elem;
};
137. Class template
As with algorithms, we can express such a similarity by turning
vector_int in a class template
138. vector_int as a class template
As a class template, we can parameterize vector_int over its
element type
template <typename T>
class vector_int {
...
};
139. vector_int β vector
Since our container is not limited to int elements anymore, we
rename it vector
template <typename T>
class vector {
...
};
140. vector_int β vector
We then subs*tute all occurrences of int with T
template <typename T>
class vector {
public:
using iterator = T*;
using const_iterator = T const*;
vector(std::size_t sz): sz(sz), elem(new T[sz]) {...}
T& operator(std::size_t i) { return elem[i]; }
T const& operator(std::size_t i) const { return elem[i]; }
...
private:
std::size_t sz;
T* elem;
};
141. vector
As a result, we obtained a non-resizable container of con5guously-
allocated elements of type T
vector<double> v(3);
v[0] = 7.1;
v[1] = 2.9;
v[2] = 5.3;
142. vector preserves regularity
Moreover, vector is as regular as its element type
// T is either a semi-regular,
// regular or totally ordered type
template <typename T>
class vector { ... }
143. vector preserves regularity
In other words, vector is a regularity-preserving type constructor
// T is either a semi-regular,
// regular or totally ordered type
template <typename T>
class vector { ... }
144. vector vs. min_element
template <typename I>
I min_element(I first, I last) { ... }
void print_min(vector<double> v) {
vector<double>::iterator first = v.begin();
vector<double>::iterator last = v.end();
vector<double>::iterator m = min_element(first, last);
if (m != last)
std::cout << "the min is " << *m;
}
145. vector vs. min_element
template <typename I>
I min_element(I first, I last) { ... }
void print_min(vector<double> v) {
auto first = v.begin();
auto last = v.end();
auto m = min_element(first, last);
if (m != last)
std::cout << "the min is " << *m;
}
147. The book regular type
class book {
public:
book() {}
book(std::string title,
std::string isbn,
int pub_year): title(title), isbn(isbn), pub_year(pub_year) {}
bool operator<(book const& b) const { return isbn < b.isbn; }
bool operator==(book const& b) const { return isbn == b.isbn; }
// definitions for !=, <, >=, <=
private:
std::string title;
std::string isbn;
int pub_year;
};
148. Finding the minimum book
Hence, we can ο¬nd the lexicographic minimum book in a vector as:
std::vector<book> bs = ...
auto m = min_element(bs.begin(), bs.end());
149. Finding the most ancient book
What if we want to ο¬nd instead the most ancient book?
class book {
public:
...
bool operator<(book const& b) const { return isbn < b.isbn; }
private:
std::string title;
std::string isbn;
int pub_year;
};
150. Finding the most ancient book
Unfortunately, there could be only one natural order for a class
class book {
public:
...
bool operator<(book const& b) const { return isbn < b.isbn; }
private:
std::string title;
std::string isbn;
int pub_year;
};
151. Finding the most ancient book
Moreover, min_element is wri+en so as to use operator <
template <typename I>
I min_element(I first, I last) {
if (first == last) return last;
I min = first; ++first;
while (first != last) {
if (*first < *min) min = first;
++first;
}
return min;
}
152. Less-than operator
However, forcing the use of operator < seems an arbitrary choice
template <typename I>
I min_element(I first, I last) {
if (first == last) return last;
I min = first; ++first;
while (first != last) {
if (*first < *min) min = first;
++first;
}
return min;
}
153. Less-than operator
This is evident if we use the func/onal nota/on3
of operator<
template <typename I>
I min_element(I first, I last) {
if (first == last) return last;
I min = first; ++first;
while (first != last) {
if (operator<(*first, *min)) min = first;
++first;
}
return min;
}
3
With the caveat that such a nota/on is not possible when the value type of I is a built-in type
154. Less-than operator
In fact, operator< is just one out of many ordering criteria
template <typename I>
I min_element(I first, I last) {
if (first == last) return last;
I min = first; ++first;
while (first != last) {
if (operator<(*first, *min)) min = first;
++first;
}
return min;
}
155. A generalized version of min_element
Therefore, the following looks like a be3er generaliza5on:
template <typename I, typename C>
I min_element(I first, I last, C cmp) {
if (first == last) return last;
I min = first; ++first;
while (first != last) {
if (cmp(*first, *min)) min = first;
++first;
}
return min;
}
156. On the nature of cmp
Since cmp is invoked, one would say that cmp is a func%on
if (cmp(*first, *min)) min = first;
157. On the nature of cmp
However, cmp is also passed as an input argument, therefore it also
looks like an object
template <typename I, typename C>
I min_element(I first, I last, C cmp) { ... }
159. Func%on object
It turns out that cmp is an object that behaves like a func3on
template <typename I, typename C>
I min_element(I first, I last, C cmp) { ... }
160. Func%on object
We refer to such objects as func%on objects4
template <typename I, typename C>
I min_element(I first, I last, C cmp) { ... }
4
Func'on objects are also called functors. However, we prefer not to use such a term, since outside the C++
community it is used to indicate a very diο¬erent concept
161. Func%on object
In order to make an object look like a func3on we need to overload
the call operator ()
163. converter func. object
Once ini'alized with the exchange rate, we can invoke the func'on
object just like a plain func'on
converter eur_to_usd(1.1);
double euros;
std::cin >> euros;
std::cout << eur_to_usd(euros);
164. The advantage of func. objects
The advantage of func/on objects is that they can be eο¬ciently
passed to func/ons and consequently invoked
template <typename I, typename C>
I min_element(I first, I last, C cmp) { ... }
165. Comparing books by pub_year
In light of this, we need a func3on object that will take two books
and verify if the ο¬rst one is older than the second5
struct by_pub_year {
bool operator()(book const& b1,
book const& b2) const {
return b1.pub_year() < b2.pub_year();
}
};
5
Here, we are assuming the existence of an access func5on named pub_year()
166. Comparing books by pub_year
With by_pub_year in place, we can ο¬nally look for the most
ancient book
std::vector<book> bs = ...
auto m = min_element(bs.begin(),
bs.end(), by_pub_year());
167. Default comparison
However, we would s-ll like to default to operator< when no
custom func-on object is passed
std::vector<book> bs = ...
auto min_isbn = min_element(bs.begin(), bs.end());
168. less func&on object
To this extent, let us ο¬rst write a func4on object to serve as the
default comparator, which we call less6
struct less {
template <typename T> // T is a totally ordered type
bool operator(T const& e1, T const& e2) const {
return e1 < e2;
}
};
6
In the C++ Standard, the type parameteriza4on is on the class, rather than on the operator overload. That is, the
standard default comparator is of type std::less<T>
169. min_element overload
Then, we provide an overload of min_element which wraps the
more general func7on
template <typename I, typename C>
I min_element(I first, I last, C cmp) { ... }Β Β Β Β Β Β Β Β Β Β Β Β Β
template <typename I>
I min_element(I first, I last) {
return min_element(first, last, less());
}
170. Finding the minimum book
Thanks to this, we are free to decide if we want to pass or not a
custom comparator
std::vector<book> bs = ...
auto min_isbn = min_element(bs.begin(), bs.end());
auto min_pub = min_element(bs.begin(), bs.end(), by_pub_year());
172. py_pub_year vs. strict total order
As a ma&er of fact, by_pub_year does not induce a strict total
order
struct by_pub_year {
...
};
173. py_pub_year vs. strict total order
Indeed, the trichotomy law would impose two books with the same
publishing date to be the same
!by_pub_year({"The dubliners", 1914}, {"The metamorphosis ", 1914}) &&
!by_pub_year({"The metamorphosis ", 1914}, {"The dubliners", 1914})
176. Strict weak ordering
A strict weak ordering is an ordering rela-on that is strict,
transi/ve and obeys the weak-trichotomy law
177. Weak-trichotomy law
An ordering rela,on r obeys the weak-trichotomy law if
!r(a, b) && !r(b, a) is the same as eq(a, b),
for some equivalent rela0on eq
178. Weak vs. total ordering
Therefore, a strict total ordering is a par-cular case of weak total
ordering in which equality is used as the equivalent rela-on
179. min_element requirements
Finally, we can state the correct requirements for min_element
// I is a forward iterator
// C defines a strict weak ordering on I's value type
template <typename I, typename C>
I min_element(I first, I last, C cmp) { ... }
181. Generic programming
The Standard Template Library is the most successful example of a
programming paradigm called generic programming7
7
With the Boost library steadily holding the second posi5on
182. Generic programming
Generic programming is an approach to programming that focuses
on designing algorithms and data structures so that they work in
the most general se0ng without loss of eο¬ciency
183. Generic programming
That is, generic programming is a way of developing so7ware that
maximizes code reuse without sacriο¬cing performance
185. Parameteriza)on
We can parameterize a type with another (such as a vector with
its element type)
template <typename T>
class vector {
...
}
186. Parameteriza)on
We can parameterize an algorithm with an other (such as the
min_element func6on with a comparison func. object)
template <typename I, typename C>
I min_element(I first, I last, C cmp) {...}
188. Polymorphism
For example, the following non-generic version of min_element is
speciο¬c to arguments of type int*
int* min_element(int* first, int* last) {...}
189. Polymorphism
On the contrary, generic func1ons are polymorphic, i.e., they
operate on any data type so long as their requirements hold
// I is a forward iterator
// C defines a strict weak ordering on I's value type
template <typename I, typename C>
I min_element(I first, I last, C cmp) {...}
190. Parametric polymorphism
This form of polymorphism is usually called parametric
polymorphism, as opposed to ad-hoc polymorphism, which is
typical of the object-oriented paradigm
191. Most general algorithms
Generic programming tries to discover polymorphic func6ons by
observing that several monomorphic func6ons all share a similar
structure
192. Most general algorithms
That is, generic programming tries to deο¬ne algorithms in terms of
the most general concepts
193. Avoid par*al solu*ons
The availability of correct generic algorithms allows developers to
avoid wri7ng their own par$ally correct ones
194. Avoid par*al solu*ons
Thanks to this, developers can avoid using statements such as for
and while that can cause non-robust behavior
195. Avoid par*al solu*ons
With min_element, we should not need any more to iterate over
a container with a for loop just for the sake of ο¬nding the
minimum element
196. Avoid par*al solu*ons
"Photoshop is roughly 3 million lines of code last 7me I counted, which
means I think we will be able to express the en7re Photoshop in about
30 thousand lines of code"
(S. Parent)
197. Before using the STL
void PanelBar::RepositionExpandedPanels(Panel* fixed_panel) {
CHECK(fixed_panel);
// First, find the index of the fixed panel.
int fixed_index = GetPanelIndex(expanded_panels_, *fixed_panel);
CHECK_LT(fixed_index, expanded_panels_.size());
// Next, check if the panel has moved to the other side of another panel.
const int center_x = fixed_panel->cur_panel_center();
for (size_t i = 0; i < expanded_panels_.size(); ++i) {
Panel* panel = expanded_panels_[i].get();
if (center_x <= panel->cur_panel_center() ||
i == expanded_panels_.size() - 1) {
if (panel != fixed_panel) {
// If it has, then we reorder the panels.
ref_ptr<Panel> ref = expanded_panels_[fixed_index];
expanded_panels_.erase(expanded_panels_.begin() + fixed_index);
if (i < expanded_panels_.size()) {
expanded_panels_.insert(expanded_panels_.begin() + i, ref);
} else {
expanded_panels_.push_back(ref);
}
}
break;
}
}
198. A"er using the STL
// Next, check if the panel has moved to the left side of another panel.
auto f = begin(expanded_panels_) + fixed_index;
auto p = lower_bound(begin(expanded_panels_), f, center_x,
[](const ref_ptr<Panel>& e, int x){ return e->cur_panel_center() < x; });
// If it has, then we reorder the panels.
rotate(p, f, f + 1);
200. Bibliography
β’ B. Stroustrup, Programming: Principles and Prac8ce
Using C++ (2nd
ed.)
β’ A. Stepanov, P. McJones, Elements of programming
β’ A. Stepanov, D. Rose, From Mathema8cs to Generic
Programming
201. Bibliography
β’ A. Stepanov, Notes on programming
β’ A. Stepanov, Eο¬cient Programming with Components Lecture 4
β’ A. Stepanov, Eο¬cient Programming with Components Lecture 11
β’ S. Parent, A Possible Future of SoCware Development
β’ S. Parent, Programming ConversaEons Lecture 5
β’ S. T. Lavavej, Standard Template Library (STL), 1 of n
202. Bibliography
β’ B. Stroustrup, C++ in 2005
β’ B. Stroustrup, What is "generic programming"?
β’ ISO C++, What's the big deal with generic programming?