Transcript Document

From Last Time: Search with Generic Iterators
• Third generalization: separate iterator type parameter
• We arrive at the find algorithm (Austern pp. 13):
template <typename Iterator, typename T>
Iterator find (Iterator first, Iterator last,
const T & value) {
while (first != last && *first != value)
++first;
return first;
}
• Which kinds of iterators will work with this algorithm?
• How can we determine that from the algorithm itself?
CSE 332: C++ Algorithms II
Key Ideas: Concepts and Models
• A concept gives a set of type requirements
– Classify/categorize types (e.g., random access iterators)
– Tells whether or not a type can or cannot be used with a
particular algorithm (get a compiler error if it cannot)
• E.g., in the examples from last time, we could not use a linked list
iterator in find1 or even find2, but we can use one in find
• Any specific type that meets the requirements is a
model of that concept
– E.g., list<char>::iterator vs. char * in find
• Different abstractions (bi-linked list iterator vs. char array iterator)
• No inheritance-based relationship between them
• But both model iterator concept necessary for find
CSE 332: C++ Algorithms II
Concepts and Models, Continued
• What very basic concept does the last statement of
find, (the line return first;) assume?
– Asked another way, what must be able to happen to first
when it’s returned from function find?
– Same requirement imposed by by-value iterator parameters
• What other capabilities are required of the Iterator
and T type parameters by the STL find algorithm ?
template <typename Iterator, typename T>
Iterator find (Iterator first, Iterator last,
const T & value) {
while (first != last && *first != value)
++first;
return first;
}
CSE 332: C++ Algorithms II
Matching an Algorithm to the Iterators it Needs
Category/
Operation
Input
Forward
Bidirectional
Random
Access
=*p
=*p
=*p
=*p
(r-value)
(r-value)
(r-value)
(r-value)
->
->
->
[]
*p=
*p=
*p=
*p=
(l-value)
(l-value)
(l-value)
(l-value)
++
++
++ --
++ -- +
- += -=
== !=
== !=
== !=
== != <
> <= >=
Output
Read
->
Access
Write
Iteration
Comparison
++
What STL iterator category does find require?
CSE 332: C++ Algorithms II
Iterator Concept Hierarchy
“destructive” read at head “transient” write to
of stream (istream)
stream (ostream)
•read or write a
Input Iterator
Output Iterator
value (one-shot)
Singly-inked-list
style access
•value persists
Forward Iterator
(forward_list)
after read/write
•values have
Bi-linked-list style
locations
Bidirectional Iterator
access (list)
•can express
is-a
distance
(refines)
Array/buffer
between two
Random
Access
Iterator
style access
iterators
(vector, deque)
CSE 332: C++ Algorithms II
What if an Algorithm Has Alternative Versions?
// Based on Austern, pp. 38, 39
• Static dispatching
template <class Iter, class Distance>
void move (Iter i, Distance d, fwd) {
while (d>0) {--d; ++i;} // O(d)
}
concrete tag (empty struct) type
template <class Iter, class Distance>
void move (Iter i, Distance d, rand) {
i += d; // O(1)
}
concrete tag (empty struct) type
– Implementations
provide different
signatures
– Iterator type is
evaluated at
compile-time
– Links to the best
implementation
template <class Iter, class Distance>
void move (Iter i, Distance d) {
move (i, d,
iterator_traits<Iter>::
iterator_category()
)
default constructor (call syntax)
}
• Notice how type
tags are used
CSE 332: C++ Algorithms II
Iterator Traits and Category Type Tags
struct
struct
struct
struct
struct
input {}; // empty structs for type tags
output {};
fwd : public input {}; // note inheritance
bidir : public fwd {};
rand : public bidir {};
(actually, random_access_iterator_tag)
template <typename I> struct iterator_traits {
...
typedef typename I::iterator_category
iterator_category;
};
• Need a few
concrete types to
use as tags
– E.g., empty structs
– E.g., input,
output, fwd,
bidir, and rand
• Tags provide yet
template <typename T> struct iterator_traits<T*> {
another associated
...
type for iterators
typedef rand iterator_category;
};
template <typename T>
struct iterator_traits<const T*> {
...
typedef rand iterator_category;
};
CSE 332: C++ Algorithms II
– Iterator category
– Again, made
available by using
the traits idiom
Can Extend STL Algorithms with Callable Objects
•
•
Make the algorithms even more general
Can be used parameterize policy
–
–
•
Each callable object does a single, specific operation
–
•
E.g., the order produced by a sorting algorithm
E.g., the order maintained by an associative containe
E.g., returns true if first value is less than second value
Algorithms often have overloaded versions
–
E.g., sort that takes two iterators (uses operator<)
–
E.g., sort that takes two iterators and a binary predicate,
uses the binary predicate to compare elements in range
CSE 332: C++ Algorithms II
Callable Objects and Adapters
•
Callable objects support function call syntax
–
A function or function pointer
bool (*PF) (const string &, const string &); // function pointer
bool string_func (const string &, const string &); // function
–
A struct or class providing an overloaded operator()
struct strings_ok {
bool operator() (const string &s, const string &t) {
return (s != “quit”) && (t != “quit”);
}
};
–
A lambda expression (unnamed inline function)
[quit_string] (const string &s, const string &t) -> bool
{return (s != quit_string) && (t != quit_string);}
•
Adapters further extend callable objects
–
–
–
E.g., bind any argument using bind and _1 _2 _3 etc.
E.g., wrap a member function using mem_fn
E.g., wrap callable object with function (associates types)
CSE 332: C++ Algorithms II
Using Functions with an Algorithm
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
using namespace std;
int main (int, char *[]) {
heap object
vector<EmployeePtr> v;
v.push_back(new Employee("Claire", 23451));
v.push_back(new Employee("Bob", 12345));
v.push_back(new Employee("Alice", 54321));
cout << "v: " ;
struct Employee {
copy (v.begin(), v.end(),
Employee (const char * n, int i) : name_(n),
ostream_iterator<EmployeePtr>(cout));
id_(i) {}
cout << endl;
string name_;
int id_;
// "v: Claire 23451 Bob 12345 Alice 54321 "
};
sort (v.begin(), v.end(), id_compare);
cout << "v: " ;
pass function name
copy (v.begin(), v.end(),
ostream_iterator<EmployeePtr>(cout));
cout << endl;
// "v: Bob 12345 Claire 23451 Alice 54321 "
typedef Employee * EmployeePtr;
ostream& operator<< (ostream & os,
const EmployeePtr & e) {
os << e->name_ << " " << e->id_ << " ";
return os;
}
// clean up: pointers "own" the heap objects
for (vector<EmployeePtr>::iterator i =
v.begin();
i != v.end(); ++i) {
delete *i;
}
return 0;
// function for comparing EmployeePtrs
bool id_compare (const EmployeePtr & e,
const EmployeePtr & f) {
return e->id_< f->id_||
(e->id_ == f->id_ && e->name_ < f->name_);
}
}
CSE 332: C++ Algorithms II
Using Function Objects with an Algorithm
• count_if algorithm
– Generalizes the
count algorithm
– Instead of
comparing for
equality to a value
– Applies a given
predicate function
object (functor)
– If functor’s result is
true, increases count
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template <typename T> // covers many types
struct odd {
bool operator() (T t) const {
return (t % 2) != 0;
}
};
int main (int, char * []) {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(2);
cout << "there are " << count_if(v.begin(),
v.end(),
odd<int>())
<< " odd numbers in v" << endl;
/* output is
there are 2 odd numbers in v
*/
return 0;
}
CSE 332: C++ Algorithms II
Concluding Remarks
• STL algorithms give you useful, generic functions
– Combine easily with a variety of containers/iterators
– Support many common data structure manipulations
• Finding and modifying values, re-ordering, numeric operations
– Reusing them saves you from writing/debugging code
• Many STL algorithms can be extended
– Especially by plugging callable objects (functors) into them
– C++11 lambdas & the bind function give new ways to do that
• Think about how iterators and algorithms combine
–
–
–
–
Think about which category of iterator a container provides
Think about the concept each iterator models
Think about the type requirements an algorithm imposes
Think about which combinations are valid, accordingly
CSE 332: C++ Algorithms II