C++ Training Datascope Lawrence D’Antonio Lecture 10 An Overview of C++: The Standard Template Library Why Should You Learn STL? STL is generic. It involves data structures and algorithms that are designed to work on as wide a range of data types as possible. STL is useful. It provides a variety of basic tools that are easy to learn and to use. STL has a well-designed architecture that provides a variety of algorithms that can manipulate a number of data structures. Why Should You Learn STL? STL is efficient. STL algorithms come with performance guarantees. For example, insertion into a map data structure is guaranteed to be at most logarithmic. STL is extensible.The components of STL may be put together to create powerful generic libraries. For example, there exists an MTL (Matrix Template Library), a GTL (Graph Template Library), and Regex++, a regular expression library based on STL. What is STL? The Standard Template Library (STL) is a powerful collection of generic data structures and algorithms that is part of the standard library in C++. The algorithms in STL are orthogonal to the data structures, in the sense that algorithms are designed to work without having to know details of the data structures to which they are being applied. What is STL? 2 In order for this process to work, data containers are required to define the concept of an iterator (i.e., objects that define an addressing mechanism for the data structure). These iterators are used in turn by the STL algorithms to access the data structure. What is STL? 3 Different algorithms require different types of access. For example, the generic sorting algorithms in the STL require random access. This restricts the data structures that sorting algorithms can be used with to ones that provide random access (in the standard library this includes arrays, strings, vectors, and deques). What is STL? 4 Alexander Stepanov, the primary developer of the STL, identifies four principles that underlie the design of STL. Generic programming Abstractness without loss of efficiency Von Neumann computational model Value semantics Generic programming Generic programming can be thought of as writing software components that can be used for as wide a range of types as possible. Stepanov states that generic programming attempts to find the most abstract form of an efficient algorithm. Generic programming 2 The central abstraction in STL is that of the concept. A concept is a set of requirements for data types. For example, a Sequence is a concept describing data containers that store elements in a linearly ordered range and allow for insertion or deletion at any point in that range. Any data structure fitting these requirements is a model for the Sequence concept. STL defines three sequence types; vectors, lists, and deques. Generic programming 3 Concepts can be classified and organized in hierarchies. For example, one of the primary concepts of the STL is that of the Iterator. Iteration allows for movement from one address location in a data structure to another. There are various categories of Iterators. Generic programming 4 A ForwardIterator only allows movement in one direction. There are two Iterator concepts that the ForwardIterator refines, the InputIterator and the OutputIterator. The InputIterator can only read from the current location and allows movement forward. The OutputIterator can only write to the current location and allows movement forward. Generic programming 5 The algorithms that form the STL are themselves organized around concepts. A generic algorithm consists of an implementation and the set of requirements that its arguments must satisfy. For example, the STL algorithm reverse() will reverse the elements of any container that is a model of the Sequence concept. Generic programming 6 Here is a non-generic gcd function. int gcd(int x, int y) { while (y != 0) { int t = x % y; x = y; y = t; } return x < 0 ? -x : x; } Generic programming 7 This algorithm is fine as it goes, but a generic version should be valid for other algebraic quantities besides integers (in fact, for any principal ideal domain). For example, we would want to extend the algorithm to polynomials and Gaussian integers. Generic programming 8 One problem arises here, the return statement, which is assuming that the gcd returns a positive value, will only be valid for types that satisfy the requirement of being totally ordered. Generic programming 9 Here is a generic gcd function. template <class T> T gcd(T x, T y) { while (y != 0) { T t = x % y; x = y; y = t; } return x; } Generic programming 10 In this case, the 'greatest' divisor that we are returning is only unique up to multiplication by a unit. Each type will have its own concept of greatest (greatest degree for polynomials, greatest modulus for Gaussian integers). Generic programming 11 To summarize (and perhaps oversimplify). object-oriented programming = data abstraction + object types + type inheritance generic programming = data abstraction + generic types + concept refinement Abstraction STL makes efficiency an important part of the design of the library. Complexity guarantees are part of interface of each STL component. For example, there are three sequence containers, vector<Type>, list<Type>, deque<Type>. The C++ standard gives the following time guarantees for element access, element insert/delete, and range insert/delete. Abstraction 2 Operation Vector Deque List Access at front Constant Constant Constant Access at back Constant Constant Constant Random access Constant Constant Linear Insert at front Linear Constant* Constant Insert at back Constant* Constant* Constant Random insert Linear Linear Constant Insert range Linear Linear Constant Abstraction 3 STL also provides associative containers that, in general, store elements of the form (key, value), in some sort of tree structure (usually a red-black tree). There are four types of associative containers: Sets (which are collections of unique keys without corresponding values). Multisets (which allow for multiple occurrences of a key). Maps (which store key, value pairs with keys being unique). Multisets (which store key, value pairs with multiple keys possible). Abstraction 4 The basic operations for associative containers are, finding an element with a given key, inserting an element, erasing an element, erasing all elements pointed to by a key. In the following table, N represents the number of elements in the container, K represents the number of values corresponding to a particular key. Abstraction 5 Operation Set Multiset Map Multimap Find Log(N) Log(N) Log(N) Log(N) Insert Log(N) Log(N) Log(N) Log(N) Erase Constant* Constant* Constant* Constant* Log(N)+K Log(N) Log(N)+K Erase Key Log(N) Von Neumann Model Stepanov notes the significance of addressing in the design of STL. The STL containers provide services for adding or deleting addresses to a data structure. Iterators can be seen as moving from one address to another in a container. Value Semantics In the STL containers own their elements. When a container is copied, all elements are copied. When a container is destroyed, all objects it contains are in turn destroyed. All copies in STL are deep copies. These value semantics imply that STL containers can never overlap. Value Semantics 2 Of course, for the sake of efficiency, one can store pointers to larger structures in a container. But this itself presents problems. One has to adapt the comparison functions that several STL containers and algorithms require to work with pointers. Value Semantics 3 A more important consideration is the use of STL with polymorphic objects. Because of the use of value semantics in STL, polymorphic objects are either sliced (i.e., only their base parts are copied or assigned) or, if pointers are used, there is a problem of ownership of the object (e.g., what should happen if the object is removed from the container). Value Semantics 4 One solution to this problem is the use a type of smart pointer. For example, one can define a pointer object containing a reference to a polymorphic object. When copying or assigning the smart pointer, a new copy of the referenced object is made (with the correct dynamic type). A Short History of STL The first explorations of the concept of generic programming go back to work of Alexander Stepanov in the late '70s. Later work at General Electric Research and Development in collaboration with David Musser led to the development of a list processing library in Ada, the first language to support generic programming. This work, in 1987, was followed by additional work in Scheme A Short History of STL 2 Stepanov, in work at Bell Laboratories and then later at Hewlett-Packard Research Laboratories, turned his attention to formulating generic algorithms in C++. He was particularly attracted to the C++ feature of templates, which allow for parametrized types. Bjarne Stroustrup, the developer of C++, introduced templates in 1991, in the cfront Release 3.0 A Short History of STL 3 While Stepanov was at Hewlett-Packard, Meng Lee joined his project and became a major contributor to the library that was eventually called STL. Andrew Koenig of Bell Laboratories, a member of the X3J16 ANSI/ISO committee for C++ standardization, invited Stepanov to present the basic ideas of the library at the November 1993 X3J16 meeting. This led to request from the committee for Stepanov and Lee to write a draft proposal for STL at the March 1994 committee meeting. A Short History of STL 4 Further revisions and extensions were proposed to the STL at this point. The major extension, associative containers, was implemented by David Musser. After this work, the STL proposal was accepted by the ANSI committee at their July 1994 meeting. The document produced by Stepanov and Lee was accepted into the X3J16 draft standard. A Short History of STL 5 The dissemination of the STL became widespread with the decision by HP, in August 1994, to make the library freely available on the Internet. This implementation, due primarily to Stepanov, Lee, and Musser, is the basis for all future versions of the STL. A Short History of STL 6 In 1996 Stepanov joined SGI and together with Matthew Austern and Hans Boehm, worked on an implementation of the STL. This implementation includes various extensions of the STL, such as thread-safe memory allocation and hash tables. It should be noted that the extensions in the SGI version of the library have not yet been accepted into the C++ standard. This implementation may be downloaded, together with extensive documentation on the library, at http://www.sgi.com/tech/stl/ STL by example First, to see the power of STL and how it functions let us look at a simple series of examples, adapted from David Harvey's online STL tutorial: http://www.davethehat.com/articles/eff_stl. htm STL by example 2 Word Copy Example The task is to write a function that reads in strings from an input stream, sorts them, eliminates duplicates, and then prints the results to an output stream, one word per line. STL by example 3 Version 1 (Non-STL) This version is given in pseudo-code to spare the reader the gory details (which they can presumably fill in for themselves). STL by example 4 ostream& wordcopy(istream &in, ostream &out) { string s; //string to be read //Also need a container to store the strings while( !in.eof() && in >> s ) { //store s in container } //Sort the container in alphabetical order //Remove duplicates, this gets ugly if you used an array //Print each string to output stream, one per line return out; } STL by example 5 Version 2 (STL using vector container) ostream& wordcopy(istream &in, ostream&out) { string s; //string to be read vector<string> v; //STL vector container for the strings while( !in.eof() && in >> s ) v.push_back(s); //inserts s at end of container STL by example 6 //STL sort algorithm sort(v.begin(), v.end()); //Use the STL unique() to 'eliminate‘ duplicates //unique 'removes' consecutive duplicates vector<string>::iterator p = unique(v.begin(), v.end()); //Now send results to output for(vector<string>::iterator i = v.begin(); i != p; i++) out << *i << '\n'; return out; } STL by example 7 A couple of comments on this version. The use of iterator is an essential part of STL. Iterators are used to traverse containers, and to add or remove items from containers. Each container-type has a corresponding iterator-type. Vectors use random-access iterators. STL by example 8 The unique algorithm has a quirk that one must get used to. Since it is a generic algorithm, and hence knows nothing about the underlying organization of the data it is being applied to, it cannot actually remove duplicates from the container. What it in fact does is to place any duplicates at the end of the container. After the algorithm is completed, there will be a range of nonduplicates, say in positions [first,last). The function will return position last. unique() algorithm template<class FwdIt> FwdIt unique(FwdIt F, FwdIt L) { FwdIt X = F; //X will step through duplicates FwdIt Fb = F; //Fb will store position of first duplicate for( *X++ = *Fb; ++F != L; ) { if( !(*Fb == *F) ) { Fb = F; *X++ = *Fb; } } return X; } unique() algorithm 2 main() { int A[ ] = {1,1,1,2,3,3}; int *p = std::unique(&A[0],&A[6]); for(int *q = &A[0]; q != p; q++) std::cout << *q << ' '; std::cout << '\n'; return 0; } unique() algorithm 3 template<class FwdIt, class BinPred> FwdIt unique(FwdIt F, FwdIt L, BinPred P) { FwdIt X = F; FwdIt Fb = F; for( *X++ = *Fb; ++F != L; ) { if( !P(*Fb,*F) ) { Fb = F; *X++ = *Fb; } } return X; } unique() algorithm 4 Consider the following predicate class P { public: bool operator()(int x, int y) { return ((x % 2) != (y%2)); } }; unique() algorithm 5 main() { int A[ ] = {1,1,1,2,3,3}; int *p = unique(&A[0],&A[6],P()); for(int *q = &A[0]; q != p; q++) std::cout << *q << ' '; std::cout << '\n'; return 0; } STL by example 9 Version 3 (STL using set container) A student studying STL quickly learns about the benefits of the STL associative containers. For the task at hand, the set container is particularly useful. Data in a set is kept in sorted order, and no duplicates are allowed. This is exactly what we want. STL by example 10 ostream& wordcopy(istream &in, ostream &out) { string s; //string to be read set<string> words; //STL set container for the strings while( !in.eof() && in >> s ) words.insert(s); //Insert in sorted order STL by example 11 //Now send results to output for(set<string>::iterator i = words.begin(); i != words.end(); i++) out << *i << '\n'; return out; } STL by example 12 Note, if the input string s is already in set, then the insertion does nothing. The insert function returns a pair consisting of an iterator and a bool. If the insertion was successful, it returns (new position, true). If the insertion failed, because the value was already in the set, the return will be (existing position, false). STL by example 13 Version 4 (STL using sets and stream adaptors) Copying operations in the STL often make use of adaptors. An adaptor makes one object act like another. A stream adaptor makes an input or output stream look like a container to an STL algorithm. In general, the copy algorithms in the library use source and destination containers. But by using adaptors, we can copy from or to a stream. STL by example 14 One difficulty in using the copy algorithms is the fact that the destination container is assumed to have the space to hold the data copied over from the source. If this is not the case, as in the example below, then an inserter object must be used for the destination (the inserter will call the underlying insert() member function). STL by example 15 ostream& wordcopy(istream &in, ostream &out) { set<string> words; //STL set container for the strings //Read in from istream and store in set copy(istream_iterator<string>(in), istream_iterator<string>(), inserter(words, words.begin())); STL by example 16 //Now send results to output copy(words.begin(), words.end(), ostream_iterator<string>(out, "\n")); return out; } STL by example 17 A few comments are in order. The adaptor, istream_iterator, needs to know the data type that is being read, string in this case. Note that the extraction operator >> is used by istream_iterator, so it can only read types for which this operator has been defined. Also, we have used a constructor to create the istream_iterator and to give it the input stream to read from. STL by example 18 The default constructor for istream_iterator is used to create an end of file position that is used to terminate the algorithm. The mechanism to make this work is a little tricky, since the default istream_iterator doesn't know what file it is supposed to be the end of! Here is the way it works. Istream iterators keep a data member that is a pointer, lets call it Istr, to the istream it traverses. STL by example 19 Suppose we declare (note the string type plays no role) two istream iterators and then increment one of them. istream_iterator<string> i(in_file); //i.Istr = &in_file istream_iterator<string> j; //j.Istr = 0 ++i; STL by example 20 The increment operator for i calls an internal read function that uses the extraction operator >>. If the extraction fails, i.Istr is set equal to 0, and then iterators i, j will compare as being equal. STL by example 21 A third comment on the above version is that the ostream_iterator constructor takes two arguments, the ostream and a string to be used as a delimiter between outputs. Iterator adaptors Let’s look in more detail at the iterator adaptors istream_iterator, ostream_iterator, insert_iterator (which inserter returns). istream_iterator template<class T, class E = char, class Tr = std::char_traits<E> > class istream_iterator { public: typedef istream_iterator<T,E,Tr> Iter; typedef E char_type; typedef Tr traits_type; typedef std::basic_istream<E,Tr> istream_type; istream_iterator 2 protected: T val; istream_type *Istr; void Getval() { if (Istr != 0 && !(*Istr >> val)) Istr = 0; } istream_iterator 3 public: istream_iterator() : Istr(0) {} istream_iterator(istream_type &I) : Istr(&I) { Getval(); } const T& operator*() const { return val; } const T* operator->() const { return &**this; } Iter &operator++() { Getval(); return *this; } Iter operator++(int) { Iter temp; Getval(); return temp; } istream_iterator 4 bool Equal(const Iter& x) const { return Istr == x.Istr; } }; template<class T, class E, class Tr> bool operator==(const istream_iterator<T,E,Tr> &x, const istream_iterator<T,E,Tr> &y) { return x.Equal(y); } template<class T, class E, class Tr> bool operator!=(const istream_iterator<T,E,Tr> &x, const istream_iterator<T,E,Tr> &y) { return !(x == y); } ostream_iterator template<class T, class E = char, class Tr = std::char_traits<E> > class ostream_iterator { public: typedef ostream_iterator<T,E,Tr> Iter; typedef T value_type; typedef E char_type; typedef Tr traits_type; typedef std::basic_ostream<E,Tr> ostream_type; protected: const E *Delim; ostream_type *Ostr; ostream_iterator 2 public: ostream_iterator(ostream_type &O, const E *D = 0) : Ostr(&O), Delim(D) { } Iter &operator=(const T&x) { *Ostr << x; if (Delim != 0) *Ostr << Delim; } ostream_iterator 3 const T& operator*() const { return *this; } Iter &operator++() { return *this; } Iter operator++(int) { return *this; } }; insert_iterator template<class C> class insert_iterator { public: typedef C container_type; typedef typename C::reference reference_type; typedef typename C::value_type value_type; protected: C *container; typename C::iterator iter; insert_iterator 2 public: insert_iterator(C &X, typename C::iterator I) :container(&X), iter(I) {} insert_iterator<C>& operator=( typename C::const_reference val) { iter = container->insert(iter,val); ++iter; return *this; } insert_iterator 3 insert_iterator<C>& operator*() { return *this; } insert_iterator<C>& operator++() { return *this; } insert_iterator<C>& operator++(int) { return *this; } }; template<class C,class Iter> insert_iterator<C> inserter(C& X, Iter I) { return insert_iterator<C>(X, C::iterator(I)); } STL by example 22 Version 5 (STL using transform() for string conversion) The problem of sorting strings is complicated by the issue of case. The versions above are case-sensitive (so "The" and "the" would both be in the output). A simple way to handle is to convert the strings to lower case as they are being read. But we would like to do this while keeping the simplicity of version 4. STL by example 23 This is where the transform algorithm comes into play. This function will apply a function (or function object), to each element in a source container, and pass the return value of this function to the destination container. STL by example 24 ostream& wordcopy(istream &in, ostream &out) { set<string> words; //STL set container for the strings //Read in from istream, convert to lower case, //Store in set transform(istream_iterator<string>(in), istream_iterator(), inserter(words, words.begin()), to_lower); STL by example 25 //Now send results to output copy(words.begin(), words.end(), ostream_iterator<string>(out, "\n")); return out; } STL by example 26 Here is the function to_lower string to_lower(const string &s) { string temp(s.size(),0); for(int i = 0; i < s.size(); i++) temp[i] = tolower(s[i]); return temp; } STL by example 27 Version 6 (STL with a different sorting criterion) In this version we want the strings sorted according to the following criterion. The strings should be printed out according to order of their word length. If two strings are the same length, then print out in alphabetical order. STL by example 28 To accomplish this we must give the set container a comparison function object. A function object (called a functor) is an object with an overloaded function call operator. STL by example 29 struct lengthcomp { bool operator() (const string &s1, const string &s2) const { return ( s1.size() < s2.size() ) || ( (s1.size() == s2.size()) && (s1 < s2) ); } }; STL by example 30 The only other change we make is: set<string,lengthcomp> words; How Much C++ Do You Need to Know for STL? Template Metaprogramming Templates can be viewed as an interpreted programming language. For example, consider the following example: template<int N> struct Factorial { enum { value = N * Factorial<N-1>::value }; }; How Much …? 2 //Specialized version template <> struct Factorial<1> { enum { value = 1 }; }; //... cout << '5!= ' << Factorial<5>::value; How Much …? 3 Traits classes Traits are a method for associating information about related types, values and functions with a template parameter type. Traits are used in several parts of the STL, but especially with iterators. Consider an example from the Nathan Myers paper that introduced the concept of traits to the C++ community. How Much …? 4 Myers invented traits to handle a certain problem with the C++ I/O library. The C I/O library is meant to work with the char type. But C++ is meant to be a reusable, extensible language. So that the design of the iostream library should, as far as possible, support the extension of the I/O routines to generic character types (different character sets, different size character types, etc.). How Much …? 5 Consider the streambuf class, which is the central class in the iostream library. In the original version of C++, streambuf was defined in the form: class streambuf { int sgetc(); //return the next character or EOF int sgetn(char*, int N); //get N characters }; How Much …? 6 Suppose we want to parametrize this class with respect to character type. We have a problem with the return type of sgetc(). If we are using a different character type, then we may need a different type to represent EOF. One solution is to also make the return type of sgetc() a type parameter. How Much …? 7 template<class charT, class intT> class basic_stream_buf { intT sgetc(); int sgetn(charT*, int N); }; How Much …? 8 This has two problems. First of all it is annoying for the user to have to remember the type of the end-offile marker. Second, and perhaps more important, is the question of how sgetc() should be written. What should it actually return in the case that the end of file is found? How Much …? 9 The traits technique works as follows. One defines a character traits class, which gives the properties (i.e., the traits) for each character type for which we want to use the iostream library. How Much …? 10 One defines the default traits class, template<class charT> struct ios_char_traits { }; The default traits class is empty because what are the traits of an arbitrary character type? One can specialize this class for specific character types. How Much …? 11 Here is the specialized version for char. struct ios_char_traits<char> { typedef char char_type; typedef int int_type; static inline int_type eof() { return EOF; } }; How Much …? 12 Here is the specialized version for wide char. struct ios_char_traits<wchar_t> { typedef wchar_t char_type; typedef wint_t int_type; static inline int_type eof() { return WEOF; } }; How Much …? 13 Here is the revised streambuf class. template <class charT> class basic_streambuf { public: typedef ios_char_traits<charT> traits_type; typedef traits_type::int_type int_type; int_type eof() { return traits_type::eof(); } int_type sgetc(); int_type sbumpc(); int_type snextc(); int sgetn(charT*, int N); }; A Short Tour of STL The components of STL Concepts Iterators Iterator Adaptors Containers Container Adaptors Algorithms Functors Allocators Basic Concepts Assignable Concept: A type is Assignable if it allows for copying objects of that type and for assigning values to variables of that type. Operations: copy constructor and operator= Models: All built-in types and STL containers, except for const types. Basic Concepts 2 Default Constructible Concept: A type is Default Constructible if it has a default constructor. Operations: Default constructor Models: All built-in types and STL containers. Basic Concepts 3 Equality Comparable Concept: A type is Equality Comparable if two values of this type can be compared for equality using the == operator, and this operator must define an equivalence relation. Operations: == and != Models: All built-in numeric and pointer types. Any STL container of the form Container<T> will be Equality Comparable if and only if T is Equality Comparable. Basic Concepts 4 Less Than Comparable Concept: A type is LessThan Comparable if two values of this type can be compared using the < operator, and this operator must define a partial ordering. The relation, x strictly divides y, is a partial order for integers that is not a total order. Operations: <, >, <=, >= Models: All built-in numeric types. Pointers storing addresses within the same array. Random Access Iterators traversing the same container. Basic Concepts 5 Strict Weakly Comparable Concept: A type is Strict Weakly Comparable if two values of this type can be compared using the < operator, and this operator defines an equivalence relation in the following sense. Two values, x and y, are considered equivalent if both x < y and y < x are false. Case-insensitive string comparison is a strict weak ordering that is not a total ordering. Basic Concepts 6 Refines: LessThan Comparable Models: All built-in numeric types. Pointers storing addresses within the same array. Random Access Iterators traversing the same container. Iterators An iterator is a generalization of the concept of a pointer. An iterator makes it possible to traverse a range of objects. Algorithms in STL generally manipulate iterators, instead of dealing with data structures directly. Iterator Concept Trivial iterator Input Iterator Output Iterator Forward Iterator Bidirectional Iterator Random Access Iterator Trivial Iterator Concept: An object is a Trivial Iterator if it can be dereferenced. A Trivial Iterator may be mutable (its dereferenced value may be modified) or constant (the dereferenced value cannot be modified). Refines: Assignable, Equality Comparable Trivial Iterator 2 Operations: Dereference: *p (p must be dereferenceable) Dereference Assignment: *p = x (p must be mutable) Member Access: p->m (p must be dereferenceable) Equality: p == q (same as &*p == &*q) Trivial Iterator 3 Models: All pointer and iterator types Input Iterator Concept: An object is an input iterator if it can be dereferenced and incremented. An input iterator must give read access to its dereferenced value, but not necessarily write access. Also, an input iterator does not necessarily support a multipass algorithm. Input iterators may be mutuable or constant. Input Iterator 2 Refines: Trivial Iterator Associated Types: iterator_traits<X>::value_type The type of the object dereferenced by iterator iterator_traits<X>::difference_type A signed integral type representing the distance from one iterator to another. Input Iterator 3 iterator_traits<X>::reference Reference to the iterator’s value type. iterator_traits<X>::pointer Pointer to the iterator’s value type. iterator_traits<X>::iterator_category One of the iterator tag types: input_iterator_tag, forward_iterator_tag, bidirectional_iterator_tag, random_access_iterator_tag Input Iterator 4 Definitions: An iterator is past-the-end if it points beyond the last element of a container. An input iterator j is reachable from input iterator i after applying ++i a number of times, we have i == j. The notation [i,j) refers to a range of iterators beginning with I and up to but not including j. Input Iterator 5 Operations: Those of Trivial Iterator together with: ++i, i++ Note: after ++i is executed it is not guaranteed that copies of the old value of i be dereferenceable. *i++ Input Iterator 6 Models: All pointer and STL iterators (except for ostream_iterator). The istream_iterator, used for traversing input streams, is an example of an input iterator that does not belong to any of the classes defined below. Output Iterator Concept: An object is an output iterator if it can store to its present location and be incremented. An output iterator must give write access to its location, but not necessarily read access. Also, an output iterator does not necessarily support a multipass algorithm. Output Iterator 2 Refines: Assignable Associated Types: Only the following iterator_category One of the iterator tag types: output_iterator_tag, forward_iterator_tag, bidirectional_iterator_tag, random_access_iterator_tag Output Iterator 3 Operations: those of Assignable together with Dereference assignment: *x = t ++x, x++ Postincrement assignment: *x++ = t Output Iterator 4 Models: front_insert_iterator, back_insert_iterator, insert_iterator, ostream_iterator Forward Iterator Concept: An object is a Forward Iterator if the iterator may be incremented and gives both read and write access (for mutable iterators). Refines: Default Constructible, Input Iterator, Output Iterator Forward Iterator 2 Associated Types: Same as Input Iterator. Operations: Same as Default Constructible and Input Iterator (but not the restrictions of Input Iterator). Models: Pointers, all STL container iterators Bidirectional Iterator Concept: An object is a bidirectional iterator if it allows for increments and decrements. It gives both read and write access (for mutable iterators). Refines: Forward Iterator Associated Types: Same as for Forward Iterator Bidirectional Iterator 2 Operations: Those of Forward Iterator together with --x, x--. Invariant: ++x; --x; or --x; ++x; are null operations Models: Pointers, all STL container iterators Random Access Iterator Concept: An object is a Random Access Iterator if it allows arbitrary jumps and pointer arithmetic. Refines: Bidirectional Iterator Associated Types: Same as for Bidirectional Iterator. Random Access Iterator 2 Operations: i += n (equivalent to performing ++i n times i -= n (equivalent to i += (-n) ) i+n, n+i Equivalent to X tmp = i; return tmp += n; i-n; Equivalent to X tmp = i; return tmp -= n; Random Access Iterator 3 More operations: i–j Returns a number n such that i == j+n i[n] Equivalent to *(i + n) Invariants: If i + n is defined then i += n; i -= n; is a null operation (similarly for i – n) Random Access Iterator 4 Models: Pointers and the iterators for vector and deque containers. Iterator Adaptors An iterator adaptor is a class that is a wrapper for an iterator. Adaptors have their own concepts (discussed below). Reverse Iterator Concept: A reverse_iterator takes a bidirectional or random access iterator and creates a new iterator that traverses in the opposite direction of usual iteration. Operations: *p, ++p, p++ (each performs a decrement), --p, p--(each performs an increment). Reverse Iterator 2 Example vector<int> v; //Suppose we place data in v copy(v.rbegin(), v.rend(), ostream_iterator<int>(cout," ")); This copies the data from to cout in reverse order. The call, v.rbegin() creates a reverse_iterator that points to the last location in v. The call, v.rend(), points to a position one before the first location in v. Istream Iterator Concept: An istream_iterator is used for traversing an input stream (allowing only read access and increments). Models: Input Iterator Ostream Iterator Concept: An ostream_iterator is used for traversing an output stream (allowing only write access and decrements). Models: Output Iterator Back Insert Iterator Concept: A back_insert_iterator takes a container x and converts assignments into x.push_back(). Frequently one uses the template function back_inserter, that returns a back_insert_iterator. Operations: *p, ++p, p++ (all do nothing except return *this). *p = t (calls x.push_back(t), where x is the container associated with p). Back Insert Iterator 2 Models: Output Iterator Front Insert Iterator Concept: A front_insert_iterator takes a container x and converts assignments into x.push_front(). Frequently one uses the template function front_inserter, that returns a front_insert_iterator. Operations: *p, ++p, p++ (all do nothing except return *this). *p = t (calls x.push_front(t), where x is the container associated with p). Front Insert Iterator 2 Models: Output Iterator Insert Iterator Concept: An insert_iterator takes a container x and converts assignments into x.insert(). Frequently one uses the template function inserter, that returns a insert_iterator. Operations: *p, ++p, p++ (all do nothing except return *this). *p = t (calls x.insert(p,t), where x is the container associated with p). Insert Iterator 2 Models: Output Iterator Example Suppose we want to read in a file of grades. multiset<int> grade_set; ifstream in_file("grades.dat"); copy(istream_iterator<int>(in_file), istream_iterator<int>(), inserter(grade_set)); Iterator traits struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag : public input_iterator_tag {}; struct bidirectional_iterator_tag : public forward_iterator_tag {}; struct random_access_iterator_tag : public bidirectional_iterator_tag {}; Iterator traits 2 template<class Iterator> struct iterator_traits { typedef typename Iterator::iterator_category iterator_category; typedef typename Iterator::value_type value_type; typedef typename Iterator::difference_type difference_type; typedef typename Iterator::pointer pointer; typedef typename Iterator::reference reference; }; Iterator traits 3 template<class T> struct iterator_traits<T*> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T& reference; }; Iterator traits 4 template<class T> struct iterator_traits<const T*> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef const T* pointer; typedef const T& reference; }; Iterator traits 5 template<class Category, class Value, class Distance = ptrdiff_t, class Pointer = Value*, class Reference = Value&> struct iterator { typedef Category iterator_category; typedef Value value_type; typedef Distance difference_type; typedef Pointer pointer; typedef Reference reference; }; Iterator traits 6 template<class Value, class Distance, class Pointer, class Reference> struct BidirectionalIterator : public Iterator<bidirectional_iterator_tag, Value,Distance,Pointer, Reference> {}; Iterator traits 7 template<class Value, class Distance, class Pointer, class Reference> struct RandomAccessIterator : public Iterator<random_access_iterator_tag, Value,Distance, Pointer,Reference> {}; Iterator traits 8 template<class Value, class Distance, class Pointer, class Reference> struct OutputIterator : public Iterator<output_iterator_tag, Value,Distance, Pointer,Reference> {}; Iterator traits 9 template<class InputIter, class Distance> void advance(InputIter &I, Distance N) { Advance(I, N, typename iterator_traits<InputIter>::iterator_category()); } Iterator traits 10 template<class InputIter, class Distance> void Advance(InputIter &I, Distance N, input_iterator_tag) { for( ; 0 < N; --N) ++I; } Iterator traits 11 template<class ForwardIter, class Distance> void Advance(ForwardIter &I, Distance N, forward_iterator_tag) { for( ; 0 < N; --N) ++I; } Iterator traits 12 template<class BidirectionalIter, class Distance> void Advance(BidirectionalIter &I, Distance N, bidirectional_iterator_tag) { for( ; 0 < N; --N) ++I; for( ; N < 0; ++N) --I; } Iterator traits 13 template<class RandomAccessIter, class Distance> void Advance(RandomAccessIter &I, Distance N, forward_iterator_tag) { I += N; } Container concepts There are several container concepts that are used in STL. There are three types of containers: general containers which are models for types of iterators, sequence containers, and associative containers. Container Concept: A Container stores other objects. The lifetimes of these objects are at most the lifetime of the Container. Refines: Assignable Associated Types X::value_type: the type of the elements of the container. Must be Assignable. Container 2 X::reference: behaves as a reference to the Container’s value type (usually just value_type&). X::const_reference: usually just const value_type& X::pointer: behaves as a pointer to the Container’s elements (usually just value_type*, but can be a smart pointer) Container 3 X::const_pointer: usually just const value_type*. X::iterator: an iterator that points to the Container’s elements. Must be at least an Input Iterator. X::const_iterator: a constant iterator that points to the Container’s elements. Container 4 X::difference_type: a signed integral type used to measure the distance between two of the Container’s iterators. X::size_type: an unsigned integral type that can represent nonnegative values of the Container’s difference type. Container 5 Operations: copy constructor, assignment (if LHS is mutable), destructor, begin(), end(), size(), max_size() (for that type of Container), empty(), swap() Models: all STL containers. Forward Container Concept: A Forward Container stores elements in a definite order. These containers support forward iterators. Refines: Container, Equality Comparable, LessThan Comparable Associated Types: same as Container, except X::iterator must be a Forward Iterator. Forward Container 2 Operations: Same as Container plus the following. ==, !=, <, >, <=, >= (the last four use a lexicographical comparison). Models: All STL containers Reversible Container Concept: A Reversible Container defines a bidirectional iterator. Refines: Forward Container Associated Types: All types associated with Container (with the requirement that iterators must be Bidirectional Iterators) plus the following: Reversible Container 2 X::reverse_iterator: A reverse iterator adaptor that has X::iterator as its base iterator type. The reverse iterator maps operator++ to operator– (and vice versa). X::const_reverse_iterator: A reverse iterator adaptor that has X::const_iterator as its base iterator type. Reversible Container 3 Operations: Same as Forward Container plus the following. rbegin() (points at last element of the container) rend() (points at one before the first element of the container) Models: All STL containers Random Access Container Concept: A Random Access Container defines a random access iterator. Operations: Those of Reversible Container plus element access, a[n]. Models: Vectors and deques. Vector Example Here is a program illustrating some ways of initializing a vector, together with a common error. The program below inserts a range of values into vectors v1, v2, v3, v4 by a variety of methods. One can insert a range of the form [first, last) via: - Constructor vector<T> v(first,last); - Assign member function. This method deletes any data previously held in the vector v.assign(first,last) Vector Example 2 - Insert member function. This method does not delete any existing data. The following inserts the range at the end of vector. Any valid location could be used as the insertion point. v.insert(v.end(), first, last); - Copy algorithm. This algorithm assumes that the space for the data being copied already exists. So the following version is incorrect if v does not already have space allocated to hold the data to be copied. The code will compile, but crash at run-time. copy(first, last, v.begin()); Vector Example 3 main() { int a[5] = {7, 3, 2, 5, 9}; std::vector<int> v1(&a[0], &a[5]); std::cout << "Vector 1: "; std::copy(v1.begin(), v1.end(), std::ostream_iterator<int>(cout," ")); cout << "\n\n"; std::vector<int> v2; v2.assign(v1.begin(),v1.end()); cout << "Vector 2: "; std::copy(v2.begin(), v2.end(), std::ostream_iterator<int>(cout," ")); cout << "\n\n"; Vector Example 4 vector<int> v3; v3.insert(v3.end(),v2.begin(),v2.end()); cout << "Vector 3: "; copy(v3.begin(), v3.end(), ostream_iterator<int>(cout," ")); cout << "\n\n"; vector<int> v4; //copy(v3.begin(), v3.end(), v4.begin()); copy(v3.begin(), v3.end(), back_inserter(v4)); cout << "Vector 4: "; copy(v4.begin(), v4.end(), ostream_iterator<int>(cout," ")); cout << "\n\n"; } Sequence Container Concept: A Sequence stores its elements in a linear ordering. Refines: Forward Container, Default Constructible Operations: Those of Forward Container plus, Default constructor, Sequence Container 2 Fill constructor c(n,x) creates container with n copies equal to x Fill constructor c(n) creates container with n elements initialized to default value, Range constructor c(i,j). Here, i and j are input iterators. Creates a sequence that is a copy of the range [i,j). Sequence Container 3 Insert: c.insert(p,x) inserts the value x before position p. Special cases: a.insert(a.begin(), x) inserts x before the first element of a. a.insert(a.end(),x) inserts x after the last element of a. Note: the iterator p need not be valid after the insert is performed. Sequence Container 4 Fill insert: a.insert(p, n, x) inserts n copies of value x before position p. This is guaranteed to be no slower than calling insert(p,x) n times. Range insert: a.insert(p, i, j) inserts a copy of the range [i,j) before position p. Sequence Container 5 Erase: a.erase(p) erases the element at position p. This assumes that p is dereferenceable. It returns an iterator to the element immediately following the one removed or else a.end() if there is no such element. Note: erase may invalidate iterators into the sequence. Sequence Container 6 Range erase: a.erase(p,q) erases all elements in the range [p,q). The return value is an iterator pointing to the element immediately after the range that was removed or a.end() if no such element exists. Sequence Container 7 Front: a.front() returns a reference to the first element in the sequence. Assumes the sequence is not empty. Models: vector, list, deque Sequence Container 8 Can the range insertion be used to copy from a container to itself? Can the range overlap with the position of the insertion? Consider the following example. Sequence Container 9 main() { std::vector<int> v; int a[ ] = {5,2,3,4,6}; v.insert(v.end(),&a[0],&a[5]); std::vector<int>::iterator p,i; p = find(v.begin(),v.end(),2); v.insert(p,v.begin(),v.end()); copy(v.begin(),v.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << '\n'; } Sequence Container 10 The output is 5523462346 How does the insert function work? vector::insert template<class InputIter> void insert(iterator p, InputIter F, InputIter L) { size_type M = F - L; size_type N = capacity(); vector::insert 2 if (M == 0) ; else if (max_size() < M + size()) throw length_error("vector too long"); vector::insert 3 else if (N < size() + M) { N = (max_size() < N + N/2) ? N + N/2 : size() + M; pointer S = Mybase::Alval.allocate(N, (void *) 0); pointer Q; Q = copy(begin(),P,S); Q = copy(F,L,Q); copy(P,end(),Q); } vector::insert 4 else if ( (size_type) (end() - P) < M ) { copy(P, end(), P+M); InputIter Mid = F; advance(Mid, end() - P); copy(Mid, L, end()); copy(F, Mid, P); } vector::insert 5 else if ( 0 < M ) { iterator Old_end() = end(); copy(Old_end - M, Old_end, end()); copy_backward(P, Old_end - M, Old_end); copy(F,L,P); } } GCC vector::insert template<typename _Tp, typename _Alloc> template<typename _ForwardIterator> void vector<_Tp,_Alloc>:: _M_range_insert(iterator __position,_ForwardIterator __first, _ForwardIterator __last, forward_iterator_tag) { GCC vector::insert 2 if (__first != __last) { size_type __n = std::distance(__first, __last); if (size_type(this->_M_impl._M_end_of_storage – this->_M_impl._M_finish) >= __n) { GCC vector::insert 3 const size_type __elems_after = end() - __position; iterator __old_finish(this->_M_impl._M_finish); if (__elems_after > __n) { std::uninitialized_copy(this->_M_impl._M_finish __n, this->_M_impl._M_finish, this->_M_impl._M_finish); this->_M_impl._M_finish += __n; std::copy_backward(__position, __old_finish - __n, __old_finish); std::copy(__first, __last, __position); } GCC vector::insert 4 else { _ForwardIterator __mid = __first; std::advance(__mid, __elems_after); std::uninitialized_copy(__mid, __last, this->_M_impl._M_finish); this->_M_impl._M_finish += __elems_after; std::copy(__first, __mid, __position); } } GCC vector::insert 5 else { const size_type __old_size = size(); const size_type __len = __old_size + std::max(__old_size, __n); iterator __new_start(this->_M_allocate(__len)); iterator __new_finish(__new_start); try { __new_finish = std::uninitialized_copy(iterator(this->_M_impl._M_start), __position, __new_start); GCC vector::insert 6 __new_finish = std::uninitialized_copy(__first, __last, __new_finish); __new_finish = std::uninitialized_copy(__position, iterator(this->_M_impl._M_finish), __new_finish); } catch(...) { std::_Destroy(__new_start,__new_finish); _M_deallocate(__new_start.base(), __len); __throw_exception_again; } GCC vector::insert 7 std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish); _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage – this->_M_impl._M_start); this->_M_impl._M_start = __new_start.base(); this->_M_impl._M_finish = __new_finish.base(); this->_M_impl._M_end_of_storage = __new_start.base() + __len; } } } Is this legal? template<class T> class my_sequence { private: //Some data public: //Assume the appropriate iterator has been defined //Consider the following insert functions void insert(iterator p, size_type n, const T &t) { std::cout << "insert(p,n,t)\n"; } template<class InputIterator> void insert(iterator p, InputIterator first, InputIterator last) { std::cout << "insert(p,first,last)\n"; } }; Example 2 main() { my_sequence<int> m; m.insert(m.begin(), 100, 5); return 0; } This is legal. But not what one might expect. The output is: insert(p,first,last) In other words, the compiler decides that m.insert(m.begin(), 100, 5); best fits the second insert function (whereas we expected to match the first). Why is that? void insert(iterator p, InputIterator first, InputIterator last) Is an exact match because the last two arguments for m.insert(m.begin(), 100, 5) are both ints. So the compiler matches the type InputIterator to int. But in the other insert function, void insert(iterator p, size_type n, const T &t) The compiler matches T to be an int. Usually size_t will be a typedef for unsigned int. So that m.insert(m.begin(), 100, 5) is not an exact match for this version of insert. Where did we go wrong? This is actually a very difficult problem to solve. There is no simple way to get the compiler to match the correct version of insert. One could specialize the class for the case when T is an int. One could overload the first insert function for cases where the second argument is an int, a long, etc. Where did we go wrong? 2 These methods are cumbersome (one has to write a lot of extra code that looks exactly the same). Another solution is to test if the parameter type InputIter is an integral type. There is a static member of the class numeric_limits called is_integer. Where did we go wrong? 3 How does that work? The class numeric_limits looks like: template<class T> class numeric_limits { public: static const bool is_integer = false; //… }; Where did we go wrong? 4 There are specialized versions of numeric_limits for each integral type. In each of these specialized classes is_integer is set equal to true. Corrected version #include<iostream> #include<limits> template<bool B> struct int_test { }; template<class T> class my_sequence { public: typedef T* iterator; private: //Some data Corrected version 2 private: void fill_insert(iterator p, unsigned int n, const T &t) { std::cout << "insert(p,n,t)\n"; } template<class InputIter> void range_insert(iterator p, InputIter first, InputIter last) { std::cout << "insert(p,first,last)\n"; } Corrected version 3 template<class Integral> void insert(iterator p, Integral n, Integral t, int_test<true>) { fill_insert(p,n,t); } template<class InputIter> void insert(iterator p, InputIter f, InputIter l, int_test<false>) { range_insert(p,f,l); } Corrected version 4 public: //Consider the following insert functions void insert(iterator p, unsigned int n, const T &t) { fill_insert(p,n,t); } template<class InputIter> void insert(iterator p, InputIter first, InputIter last) { insert(p,first,last, int_test<std::numeric_limits<InputIter>::is_integer>()); } }; Corrected version 5 main() { my_sequence<int> m; m.insert(m.begin(), 100, 5); return 0; } //Output is now insert(p,n,t) Front Insertion Sequence Concept: A container is a Front Insertion Sequence if it allows insertion/deletion of an element at the beginning of a sequence in constant time. Refines: Sequence Operations: In addition to the Sequence operations we have the following: Front Insertion Sequence 2 Push front: a.push_front(t) is equivalent to a.insert(a.begin(),t) Pop front: a.pop_front() is equivalent to a.erase(a.begin(),t). Has a void return. Note: push_front followed by pop_front is a null operation. Back Insertion Sequence Concept: A container is a Back Insertion Sequence if it allows insertion/deletion of an element at the end of a sequence in constant time. Refines: Sequence Operations: In addition to the Sequence operations we have the following: Back Insertion Sequence 2 Back: a.back() returns a reference or const_reference to the final element of a (depending on whether the container a is const). Push back: a.push_back(t) is equivalent to a.insert(a.end(), t) Pop back: a.pop_back() is equivalent to a.erase(--a.end()). Has a void return. Associative Container Concept: An Associative Container accesses elements in the container based on keys. Keys are kept in a sorted order (where the user may supply the sorting criterion). The keys and associated values are stored with a tree data structure (in particular, a red-black tree is the usual implementation). Since the sorted order of the keys is an invariant for this container, insertions are not allowed to specify the position of insertion. Associative Container 2 Refines: Forward Container, Default Constructible Associated Types: those of Forward Container together with, X::key_type the type of the key associated with X::value_type Associative Container 3 Operations: Same as Forward Container together with the following Default Constructor Find: a.find(k) returns an iterator pointing to an element whose key is k. Returns a.end() if key not found. Count: a.count(k) returns the number of elements whose key is k. Associative Container 4 Equal Range: a.equal_range(k) returns a pair P of iterators with key equal to k. The range is [P.first,P.second). Erase key: a.erase_key(k) erase all elements whose key is k. Erase element: a.erase(p) erases the element pointed to by p. Erase range: a.erase(i,j) erases the elements in the range [i,j) Associative Container 5 Models: set, multiset, map, multimap Associative Container FAQ What does end() return for an associative container? end() returns an iterator to a special tree node Head. This node contains no data, its left pointer points at the smallest element in the tree, its right pointer points at the largest element in the tree, its parent is the root. Associative Container FAQ 2 How do you increment an iterator for an Associative Container? Such iterators internally store a node pointer Ptr. void increment() { if ( Isnil(Ptr) ) ; else if ( !Isnil(Right(Ptr)) ) Ptr = Min(Right(Ptr)); else { Nodeptr P; while ( !Isnil(P = Parent(Ptr)) && Ptr == Right(P) ) Ptr = P; Ptr = P; } } Associative Container FAQ 3 How do lower_bound(), upper_bound() and equal_range() work? Lower_bound finds the first of an equivalent sequence of keys. Upper_bound finds the first element past an equivalent sequence of keys. Equal_range returns the pair of iterators, lower_bound and upper_bound. Nodeptr Lbound(const key_type &k) const { Nodeptr X = Root(); Nodeptr Y = Head; while( !Isnil(X) ) if( comp(Key(X),k) ) X = Right(X); else { Y = X; X = Left(X); } return Y; } Nodeptr Ubound(const key_type &k) const { Nodeptr X = Root(); Nodeptr Y = Head; while( !Isnil(X) ) if( comp(k,Key(X)) ) { Y = X; X = Left(X); } else X = Right(X); return Y; } Unique Associative Container Concept: A Unique Associative Container is an Associative Container with the property that each key in the container is unique. An element doesn’t get inserted into a container if its key is already present. Refines: Associative Container Unique Associative Container 2 Operations: Same as Associative Container plus, Range constructor: X a(i,j) creates an associative container that includes all elements in [i,j) that have unique keys. Unique Associative Container 3 Insert element: a.insert(t) inserts the element if and only if t’s key is not already in container. Returns pair<iterator,bool>. If element is already in container, iterator points to an element in the container with that key and the bool is false. If the element not in the container then the iterator points to where the element is inserted and the bool is true. Unique Associative Container 4 Insert range: a.insert(i,j) inserts all the elements in [i,j) whose key is not already in the container. Models: set, map Multiple Associative Container Concept: A Multiple Associative Container is an Associative Container that may contain more than one element with the same key. Refines: Associative Container Operations: Those of Associative Container together with the following, Multiple Associative Container 2 Range constructor: X a(i,j) creates an associative container that contains all the elements in the range [i,j). Insert element: a.insert(t) inserts t and returns a pointer to the newly inserted element. Insert range: a.insert(i,j) inserts all the elements in the range [i,j). Multiple Associative Container 3 Models: multiset, multimap Simple Associative Container Concept: A Simple Associative Container is an Associative Container whose elements are their own keys. A value stored in such a container is just a key, not a key plus additional data. Refines: Associative Container Simple Associative Container 2 Associated Types: Those of Associative Container together with: X::iterator. Mutable iterators are not allowed for Simple Associative Containers. Both X::iterator and X::const_iterator must have the same behavior. Invariant: Elements are immutable. They cannot be modified in place (but can be removed). Simple Associative Container 3 Models: set and multiset. Pair Associative Container Concept: A Pair Associative Container associates a key with some other object. Refines: Associative Container Associated Types: Those of Associative Container together with X::key_type the type of an element’s key. X::mapped_type the type of an element’s data. The container associates keys and values of type mapped_type. Pair Associative Container 2 X::value_type the type of object stored in the container. The value type is pair<const key_type, mapped_type>. Note the use of const key_type. This happens because the keys in a Pair Associative Container are immutable. The mapped_type part of an element may be modified but not the key. Pair Associative Container 3 X::iterator. A Pair Associative Container cannot provide mutable iterators. This is because the value type of a mutable iterator must be Assignable and pair<const key_type, mapped_type> is not Assignable. However iterators are not constant since you are allowed to have expressions such as i->second = val. Pair Associative Container 4 Models: map and multimap Sorted Associative Container Concept: A Sorted Associative Container sorts elements by key, using a Strict Weak Ordering. Refines: Associative Container and Reversible Container. Associated Types: Those of Associative and Reversible Container together with the following: Sorted Associative Container 2 X::key_compare is a function object type. It uses a Strict Weak Ordering to compare keys. It takes two arguments of type X::key_type and returns a bool. X::value_compare is a function object that compares two objects of value_type by passing the keys associated with these objects to a function object of type key_compare. Sorted Associative Container 3 Operations: Those of Associative Container and Reversible Container together with: Default constructor: X() creates an empty container using key_compare() as the comparison function. Default constructor with comparison: X(c) creates an empty container with comparison function c. Sorted Associative Container 4 Range constructor: X(i,j) is equivalent to X a; a.insert(i,j); Range constructor with comparison: X(i,j,c) is equivalent to X a(c); a.insert(i,j); Sorted Associative Container 5 Key comparison: a.key_comp() returns a’s key comparison object. Value comparison: a.value_comp() returns a’s value comparison object. Lower bound: a.lower_bound(k) returns an iterator to the first element whose key is not less than k. It returns a.end() if no such element is present. Sorted Associative Container 6 Upper bound: a.upper_bound(k) returns an iterator to the first element whose key is greater than k. It returns a.end() if no such element exists. Sorted Associative Container 7 Equal range: a.equal_range(k) returns a pair P such that P.first is a.lower_bound(k) and P.second is a.upper_bound(k). If there are no elements equal to k then an empty range is returned that indicates the position where those elements would have been if they did exist. All elements with key k will be contained in the range [P.first, P.second). Equal range example #include <iostream> #include <set> #include <algorithm> #include <iterator> #include <utility> main() { std::multiset<int> s; s.insert(5); s.insert(3); s.insert(12); s.insert(6); s.insert(6); s.insert(10); Equal range example 2 std::copy(s.begin(), s.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << '\n'; typedef std::multiset<int>::const_iterator CI; Equal range example 3 std::pair<CI,CI> pi = s.equal_range(6); std::copy(pi.first, pi.second, std::ostream_iterator<int>(std::cout, " ")); std::cout << '\n'; Equal range example 4 pi = s.equal_range(11); if (pi.first == s.end()) std::cout << "equal_range returned end()\n"; if (pi.second == s.end()) std::cout << "equal_range returned end()\n"; std::cout << "equal_range.first = " << *pi.first << '\n'; std::cout << "equal_range.second = " << *pi.second << '\n'; Equal range example 5 std::copy(pi.first, pi.second, std::ostream_iterator<int>(std::cout, " ")); std::cout << '\n'; return 0; } Equal range example 6 Output 3 5 6 6 10 12 66 equal_range.first = 12 equal_range.second = 12 Example Here is a small music database. We will load the database and then do a simple query, “Print out all songs sung by Billie Holiday in the database.” Artist Billie Holiday Billie Holiday Billie Holiday Bix Beiderbecke Bix Beiderbecke Ruth Etting Bunny Berigan Louis Armstrong Louis Armstrong Jack Jenney Song Mean to Me Summertime I Can’t Get Started In a Mist Singin’ the Blues Singin’ the Blues I Can’t Get Started Chinatown Stardust Stardust #include<iostream> #include <map> #include <string> using namespace std; typedef string artist; typedef string song; typedef multimap<artist,song>::iterator music_iterator; main() { multimap<artist,song> music; music.insert(make_pair("Billie Holiday", "Mean to Me")); music.insert(make_pair("Billie Holiday", "Summertime")); music.insert(make_pair("Billie Holiday", "I Can\'t Get Started")); music.insert(make_pair("Bix Beiderbecke", "Singin\' the Blues")); music.insert(make_pair("Bix Beiderbecke", "In a Mist")); music.insert(make_pair("Ruth Etting", "Singin\' the Blues")); music.insert(make_pair("Bunny Berigan", "I Can\'t Get Started")); music.insert(make_pair("Louis Armstrong", "West End Blues")); music.insert(make_pair("Louis Armstrong", "Stardust")); music.insert(make_pair("Louis Armstrong", "Chinatown")); music.insert(make_pair("Jack Jenney", "Stardust")); music.insert(make_pair("Sidney Bechet", "Summertime")); music.insert(make_pair("Sidney Bechet", "Wild Man Blues")); music.insert(make_pair("Cootie Williams", "West End Blues")); //Find songs by Billie Holiday pair<music_iterator,music_iterator> p = music.equal_range("Billie Holiday"); //Print out songs by Billie Holiday cout << "Songs by Billie Holiday\n"; for(music_iterator i = p.first, i != p.second; i++) cout << i->second << '\n'; return 0; } OUTPUT Songs by Billie Holiday Mean to Me Summertime I Can't Get Started Allocator Concept: Manages the details of memory allocation and deallocation. Refines: Equality Comparable and Default Constructible. Associated Types: X::value_type the type that the allocator manages Allocator 2 X::pointer is a pointer (not necessarily a built-in pointer type) to X::value_type. The pointer type must be convertible to const_pointer, void*, and value_type*. X::const_pointer is a const pointer to X::value_type. X::reference is a reference to X::value_type (must be X::value_type&). Allocator 3 X::const_reference is a const reference to X::value_type (must be const X::value_type&). X::difference_type is a signed integral type that represents the difference of two values of type X::pointer. X::size_type is an unsigned integral type that can represent any non-negative value of type X::difference_type. Allocator 4 X::rebind<U>::other is an allocator whose type is U. Operations: Default constructor: X() creates a default allocator. Copy constructor: X b(a) creates a copy of a (and compares equal to a). Allocator 5 Generalized copy constructor: X a(y) creates a copy of y, an Allocator of value type U. Comparison: a == b returns true if and only if the storage allocated by a can be deallocated using b and vice versa. Allocate: a.allocate(n) allocates n*sizeof(T) bytes and returns a pointer to an uninitialized block of memory. Allocator 6 Allocate with hint: a.allocate(n,p) allocates n*sizeof(T) bytes. The pointer p is a hint to where the memory may be allocated (the hint may be simply ignored). Returns a pointer to an uninitialized block of memory. Deallocate: a.deallocate(p, n) frees the memory for n objects of type T stored at location p. The pointer p must have been obtained from a previous call to allocate. Allocator 7 Maximum size: a.max_size returns the maximum number of elements that may be passed as the first argument to allocate. Construct: a.construct(p, val) constructs an object of type T at position p. Equivalent to the placement new operator new( (void *) p) T(val). Allocator 8 Destroy: a.destroy(p) destroys the object pointed to by p. Equivalent to ( (T*) p) -> ~T() Address: a.address(t) returns of object t (in the form of a pointer or const pointer) Container Adaptors A container adaptor is a class that provide a limited number of container operations. There are three container adaptors in STL: stack, queue, and priority queue. These classes are implemented in terms of underlying containers. Stack Concept: A stack adapts a sequence container (deque by default). It permits insertion, deletion, or inspection only at one end of the sequence (designated the top of the stack). Stacks do not have iterators. Template parameters: stack<T,C>. Here T is the type of element stored in stack and C is the underlying sequence container. Stack 2 Members stack::value_type is the type of object stored in the stack. Default constructor: stack::stack() declares an empty stack. Constructor with sequence: stack::stack(c) declares a stack having the same elements as container c. Stack 3 Copy constructor, destructor, assignment operator, size(), empty() top() returns a mutable reference to the top of the stack. It actually returns c.back(). push(x) inserts x at the top of the stack. Calls c.push_back(x). Stack 4 pop() removes (but does not return) the element at the top of the stack. Calls c.pop_back(). Type Requirements: Container C is a model of Back Insertion Sequence. Queue Concept: A queue adapts a sequence container (deque by default). A queue is a model of a “first in first out” container. Elements are inserted at the back and removed from the front of the sequence. Iterators are not allowed on queues. Template parameters: queue<T,C>. Here T is the type of element stored in stack and C is the underlying sequence container. Queue 2 Members: queue::value_type is the type of object stored in the queue. Default constructor: queue::queue() declares an empty queue. Constructor with sequence: queue:: queue(c) declares a queue having the same elements as container c. Queue 3 Copy constructor, destructor, assignment operator, size(), empty() front() returns the element at the front of the queue. Calls c.front(). back() returns the element at the back of the queue. Calls c.back(). push(x) inserts x at the back of the queue. No value is returned. Calls c.push_back(x). Queue 4 pop() removes the element at the front of the queue. Type Requirements: Container C is a model for Back Insertion Sequence and Front Insertion Sequence. Priority Queue Concept: A priority queue adapts a sequence container (vector by default). It provides for insertion of elements, and inspection and removal of the top element. Iterators and the modification of elements are not permitted. The top element is always the largest in the queue (elements are ordered using < by default). The elements are stored in a heap. Priority Queue 2 Template Parameters: priority_queue<T,C,Compare>. T is the type of element stored in queue. C is the underlying sequence container. Compare is the comparison function used to find the largest element in the queue. Priority Queue 3 Members: priority_queue::value_type is the same as T. Default constructor creates a priority queue with the default container and compare function. Constructor with compare function: explicit priority_queue::priority_queue(Compare comp). Priority Queue 4 Constructor with comparison and sequence: priority_queue::priority_queue(Compare comp, C cont). Range constructor with comparison, range constructor with comparison and container. Priority Queue 5 Destructor, assignment operator, size(), empty(). top() returns a const reference to the largest element of the queue (which would be the root of the heap). Calls c.front(). push(x) inserts x into the queue. Calls c.push_back() and then “bubbles up” the heap. Priority Queue 6 pop() removes the element at the top of the queue. Switches the top and bottom elements of the heap, rebuilds the hea, and then calls c.pop_back(). Type Requirements: Container must be a Random Access Container. Compare must be a Strict Weak Ordering. Functors A functor, or function object, is any object that can be used using function call syntax. A function pointer is a functor, as is any object of a class that overloads the function call operator, i.e., operator(). A pointer to a member function is not a functor. The standard library functors are limited to generators, unary and binary functions, but still provide a useful starting point for someone writing their own functors. Functors 2 A unary function returning a bool is called a Predicate. A binary function returning a bool is called a Binary Predicate. An adaptable functor has typedefs for its argument types and return type. Adaptable functors can be used by function adaptors. Generator Concept: A Generator is a function object called with no arguments. Refines: Assignable Associated Types: Result type, the type returned when the Generator is called. Operations: function call f(). Models: any function R (*)(). Unary Function Concept: A Unary Function is a function object that is called with a single argument. Refines: Assignable Associated Types: Argument type: Argument type is the type of the function’s argument. Unary Function 2 Result type: result_type is the type of the return value. Operation: function call f(x) Models: any function R (*)(T) Binary Function Concept: A Binary Function is a function object that is called with two arguments. Refines: Assignable Associated Types: Argument types: First argument type and Second argument type are the types of the function’s arguments. Binary Function 2 Result type: Result type is the type of the return value. Operation: function call f(x,y) Models: any function R (*)(T1,T2) Adaptive Generator Concept: An Adaptive Generator is a Generator that has a nested typedef for the return type. Refines: Assignable Associated Types: f::result_type, the type returned when the Generator is called. Operations: function call f(). Models: None in standard library Adaptive Unary Function Concept: An Adaptive Unary Function is a Unary Function that has nested typedefs for the argument and return types. Refines: Assignable Associated Types: Argument type: f::argument_type is the type of the function’s argument. Adaptive Unary Function 2 Result type: f::result_type is the type of the return value. Operation: function call f(x) Models: negate, logical_not, pointer_to_unary_function. Adaptive Binary Function Concept: An Adaptive Binary Function is a Binary Function with nested typedefs for the argument and return types. Refines: Assignable Associated Types: Argument types: f::first_argument_type and f::second_argument_type are the types of the function’s arguments. Adaptive Binary Function 2 Result type: result_type is the type of the return value. Operation: function call f(x,y) Models: plus, minus, multiplies, divides, modulus, equal_to, not_equal_to, greater, less, greater_equal, less_equal, logical_and, logical_or, pointer_to_binary_function. Predicate Concept: A Predicate is a Unary Function whose return is a bool. Refines: Unary Function Associated Type: the Result type must be convertible to bool. Models: Any function of the form bool (*)(T) Binary Predicate Concept: A Binary Predicate is a Binary Function that returns a bool. Refines: Binary Function Associated Types: the Result type must be a bool. Models: Any function of the form bool (*)(T1,T2) Adaptive Predicate Concept: An Adaptive Predicate is a Predicate with nested typedefs for the argument and return types. Refines: Predicate, Adaptive Unary Function Models: logical_not Adaptive Binary Predicate Concept: An Adaptive Binary Predicate is a Binary Predicate with nested typedefs for the argument and return types. Refines: Binary Predicate, Adaptive Unary Function Models: equal_to, not_equal_to, greater, less, greater_equal, less_equal, logical_and, logical_or Example Here is an example of a user defined adaptive binary predicate. This functor returns true if string s1 contains string s2 as a substring. class str_contains : public binary_function<string,string,bool>{ public: bool operator() (const string &s1, const string &s2) { return s1.find(s2) != string::npos; } }; RNG Example Here is an adaptive unary functor that was almost part of STL. This is an example of a subtractive random number generator. RNG Example 2 class subtractive_rng : public std::unary_function<unsigned int, unsigned int> { private: unsigned int _M_table[55]; size_t _M_index1; size_t _M_index2; public: // Returns a number less than the argument. unsigned int operator()(unsigned int __limit) { _M_index1 = (_M_index1 + 1) % 55; _M_index2 = (_M_index2 + 1) % 55; _M_table[_M_index1] = _M_table[_M_index1] _M_table[_M_index2]; return _M_table[_M_index1] % __limit; } RNG Example 3 void _M_initialize(unsigned int __seed) { unsigned int __k = 1; _M_table[54] = __seed; size_t __i; for (__i = 0; __i < 54; __i++) { size_t __ii = (21 * (__i + 1) % 55) - 1; _M_table[__ii] = __k; __k = __seed - __k; __seed = _M_table[__ii]; } for (int __loop = 0; __loop < 4; __loop++) { for (__i = 0; __i < 55; __i++) _M_table[__i] = _M_table[__i] - _M_table[(1 + __i + 30) % 55]; } _M_index1 = 0; _M_index2 = 31; } RNG Example 4 // Ctor allowing you to initialize the seed. subtractive_rng(unsigned int __seed) { _M_initialize(__seed); } // Default ctor; initializes its state with some number you don't see. subtractive_rng() { _M_initialize(161803398u); } }; main() { subtractive_rng s; for(int i = 0; i < 20; i++) std::cout << s(10) << ' '; std::cout << '\n'; } RNG Example 5 Here is the output from the above program. 32167078093217693106 Function adaptors A function adaptor converts one type of function object into another type. For example, the functions bind1st and bind2nd convert an Adaptable Binary Function into an Adaptable Unary Function. Class binder1st Takes an Adaptable Binary Function F(x,y) and converts it into an Adaptable Unary Function f(y) = F(c,y) where c is a constant. The function bind1st returns a binder1st object. For example, bind1st(equal_to<int>(),10) returns a function object which acts like equal_to(10,y). Class binder1st 2 template <class _Operation> class binder1st : public unary_function< typename _Operation::second_argument_type, typename _Operation::result_type> { protected: _Operation op; typename _Operation::first_argument_type value; Class binder1st 3 public: binder1st(const _Operation& __x, const typename _Operation::first_argument_type& __y) : op(__x), value(__y) { } typename _Operation::result_type operator() (const typename _Operation::second_argument_type& __x) const { return op(value, __x); } }; Class binder1st 4 template <class _Operation, class _Tp> inline binder1st<_Operation> bind1st(const _Operation& __fn, const _Tp& __x) { typedef typename _Operation::first_argument_type _Arg1_type; return binder1st<_Operation>(__fn, _Arg1_type(__x)); } Class binder2nd Takes an Adaptable Binary Function F(x,y) and converts it into an Adaptable Unary Function f(x) = F(x,c) where c is a constant. The function bind2nd returns a binder2nd object. For example, bind2nd(equal_to<int>(),10) returns a function object which acts like equal_to(x,10). Binder example We have a list of student grades. We want to copy the grades which are below 60 into a failures list. Since STL does not provide a copy_if function we use the next best thing, the remove_copy_if function. This function copies all elements from a source range for which a given predicate evaluates as false (i.e., it 'removes' the ones which make the predicate true). Binder example 2 So we will remove_copy_if() all grades which are greater than or equal to 60. There is a built-in greater_equal binary predicate. We will bind its second argument to 60, to turn it into the unary predicate that we want to use. Binder example 3 main() { int a[10] = { 50, 80, 60, 40, 30, 90, 100, 20 ,40 ,70 }; vector<int> grades(&a[0], &a[10]); vector<int> failures; remove_copy_if(grades.begin(), grades.end(), back_inserter(failures), bind2nd(greater_equal<int>(),60)); cout << "Failing grades\n"; copy(failures.begin(), failures.end(), ostream<int>(cout, “ ")); return 0; } Binder example 4 Output Failing grades 50 40 30 20 40 Class pointer_to_unary_function Takes a pointer to a unary function and converts it into an Adaptable Unary Function. The function ptr_fun returns a pointer_to_unary_function object. pointer_to_unary_function 2 template <class _Arg, class _Result> class pointer_to_unary_function : public unary_function<_Arg, _Result> { protected: _Result (*_M_ptr)(_Arg); public: pointer_to_unary_function() { } pointer_to_unary_function 3 explicit pointer_to_unary_function(_Result (*__x)(_Arg)) : _M_ptr(__x) { } _Result operator() (_Arg __x) const { return _M_ptr(__x); } }; pointer_to_unary_function 4 template <class _Arg, class _Result> inline pointer_to_unary_function<_Arg, _Result> ptr_fun(_Result (*__x)(_Arg)) { return pointer_to_unary_function<_Arg, _Result>(__x); } Example Suppose we want to transform the elements of a vector into the negative of their absolute value. Example 2 vector<int> v; //… transform(v.begin(), v.end(), v.begin(), compose1(negate<int>(), ptr_fun(fabs))); Example 3 Here compose1 is a function returning the function adaptor, unary_compose. The function unary_compose takes two functions f, g and returns their composition h defined by h(x) = f(g(x)). These elements, compose1 and unary_compose are not in the C++ standard. Example 4 template <class OP1, class OP2> class unary_compose: public std::unary_function<typename OP2::argument_type, typename OP1::result_type> { private: OP1 op1; // process: op1(op2(x)) OP2 op2; Example 5 public: // constructor unary_compose(const OP1& o1, const OP2& o2) : op1(o1), op2(o2) { } // function call typename OP1::result_type operator()(const typename OP2::argument_type& x) const { return op1(op2(x)); } }; Example 6 template <class OP1, class OP2> inline unary_compose<OP1,OP2> compose1(const OP1& o1, const OP2& o2 { return unary_compose<OP1,OP2>(o1,o2); } Class pointer_to_binary_function Takes a pointer to a binary function and converts it into an Adaptable Binary Function. The function ptr_fun returns a pointer_to_binary_function object. Class pointer_to_binary_function 2 template <class _Arg1, class _Arg2, class _Result> class pointer_to_binary_function : public binary_function<_Arg1, _Arg2, _Result> { protected: _Result (*_M_ptr)(_Arg1, _Arg2); public: pointer_to_binary_function() { } Class pointer_to_binary_function 3 explicit pointer_to_binary_function( _Result (*__x)(_Arg1, _Arg2)) : _M_ptr(__x) { } _Result operator()(_Arg1 __x, _Arg2 __y) const { return _M_ptr(__x, __y); } }; Class pointer_to_binary_function 4 template <class _Arg1, class _Arg2, class _Result> inline pointer_to_binary_function<_Arg1, _Arg2, _Result> ptr_fun(_Result (*__x)(_Arg1, _Arg2)) { return pointer_to_binary_function<_Arg1, _Arg2, _Result>(__x); } Example list<char *> L; list<char *>::iterator li = find_if(L.begin(), L.end(), not1(binder2nd(ptr_fun(strcmp), “OK”))); Class unary_negate Class unary_negate is an Adaptable Predicate that represents logical negation of some other Adaptable Predicate. If f is a unary_negate object constructed with predicate pred, then f(x) returns !pred(x). The function not1 returns a unary_negate object. Class binary_negate Class binary_negate is an Adaptable Binary Predicate that represents logical negation of some other Adaptable Binary Predicate. If f is a binary_negate object constructed with predicate pred, then f(x,y) returns !pred(x,y). The function not2 returns a binary_negate object. Example struct U : public binary_function<U,U,bool> { int id; bool operator() (const U &x, const U &y) { return x.id >= y.id; } }; Example 2 main() { vector<U> v; //Sort v in ascending order. sort(v.begin(), v.end(), not2(U())); } Class mem_fun_t Class mem_fun_t is a function adaptor that takes a member function with signature R X::f() and makes it possible to call it as if it were an ordinary function. If F is a mem_fun_t that was constructed to use X::f and x is an X* pointer then F(x) is equivalent to x->f(). The function mem_fun returns a mem_fun_t object. Class mem_fun_t 2 There are several similar function objects. The class const_mem_fun_t adapts a member function of the form R X::f() const. The class mem_fun_ref_t has its operator() take an X& argument. So that F(x) is equivalent to x.f(). The corresponding function is mem_fun_ref(). The class const_mem_fun_ref_t adapts a const member function. Class mem_fun_t 3 template <class _Ret, class _Tp> class mem_fun_t : public unary_function<_Tp*, _Ret> { public: explicit mem_fun_t(_Ret (_Tp::*__pf)()) : _M_f(__pf) {} _Ret operator()(_Tp* __p) const { return (__p->*_M_f)(); } private: _Ret (_Tp::*_M_f)(); }; Class mem_fun_t 4 template <class _Ret, class _Tp> inline mem_fun_t<_Ret, _Tp> mem_fun(_Ret (_Tp::*__f)()) { return mem_fun_t<_Ret, _Tp>(__f); } Example struct B { virtual void print() = 0; }; struct C: public B { void print() { std::cout << "I'm a C\n"; } }; struct D: public B { void print() { std::cout << "I'm a D\n"; } }; Example 2 main() { std::vector<B*> vbp; vbp.push_back(new C); vbp.push_back(new D); vbp.push_back(new C); vbp.push_back(new D); std::for_each(vbp.begin(), vbp.end(), std::mem_fun(&B::print)); Example 3 std::vector<C> vb; vb.push_back(C()); vb.push_back(C()); std::for_each(vb.begin(), vb.end(), std::mem_fun_ref(&B::print)); Example 4 std::vector<std::vector<int> > vi; vi.push_back(std::vector<int>(2)); vi.push_back(std::vector<int>(7)); vi.push_back(std::vector<int>(6)); transform(vi.begin(),vi.end(), std::ostream_iterator<int>(std::cout, " "), std::mem_fun_ref(&std::vector<int>::size)); std::cout << '\n'; Example 5 Output I'm a C I'm a D I'm a C I'm a D I'm a C I'm a C 276 Class mem_fun1_t Class mem_fun1_t is a function adaptor that takes a member function with signature R X::f(A) and makes it possible to call it as if it were an ordinary function. If F is a mem_fun1_t that was constructed to use X::f, a is a value of type A and x is an X* pointer then F(x) is equivalent to x->f(a). The function mem_fun returns a mem_fun1_t object. Class mem_fun1_t 2 There are also function objects mem_fun1_ref_t, const_mem_fun1_t, and const_mem_fun1_ref_t. STL Algorithms for_each() Function for_each(InputIterator beg, InputIterator end, Function f) - Applies function f to each element in range [beg,end) - Returns f Nonmutating Algorithms size_t count(InputIterator beg, InputIterator end, const T &val) - Returns the number of occurrences of val in range [beg,end) size_t count(InputIterator beg, InputIterator end, const T &val, Size &n) - Adds the number of occurrences of val in [beg,end) to n STL Algorithms 2 size_t count_if(InputIterator beg, InputIterator end, Predicate pred) - Returns the number of elements in range [beg,end) for which pred is true InputIterator min_element(InputIterator beg, InputIterator end) InputIterator max_element(InputIterator beg, InputIterator end) - Returns the minimum or maximum element in range [beg,end) STL Algorithms 3 bool lexicographical_compare(InputIterator1 beg1, InputIterator1 end1, InputIterator2 beg2, InputIterator end2) (Bin. Pred.) - Returns whether or not the elements in the range [beg1,end1) are less than the elements in the range [beg2,end2) Search Functions InputIterator find(InputIterator beg, InputIterator end, const T &val) - Finds and returns the first position of val in range [beg,end) - Returns end if val not found STL Algorithms 4 InputIterator adjacent_find(InputIterator beg, InputIterator end) (Bin. Pred.) - Finds and returns the first position in range [beg,end) for which two consecutive elements are equal - Returns end if no match found InputIterator find_if(InputIterator beg, InputIterator end, Predicate pred) - Finds and returns the first position in range [beg,end) for which pred is true - Returns end if no matching element found STL Algorithms 5 InputIterator adjacent_find_if(InputIterator beg, InputIterator end, Predicate pred) - Finds and returns the first position in range [beg,end) for which pred is true for two consecutive elements - Returns end if no match found InputIterator search_n(InputIterator beg, InputIterator end, Size n, const T &val) (Bin Pred) - Finds and returns the first position in range [beg,end) for which n consecutive vals occur, in the first case - Returns end if no matching element found STL Algorithms 6 ForwardIterator1 search(ForwardIterator1 beg1, ForwardIterator1 end1, ForwardIterator2 beg2, ForwardIterator2 end2) (Bin Pred) - Returns the position of the first element of the first subrange in the range [beg1,end1) that matches all of the elements in the range [beg2,end2) - Returns end1 if no match found ForwardIterator1 find_end(ForwardIterator1 beg1, ForwardIterator1 end1, ForwardIterator2 beg2, ForwardIterator2 end2) (Bin Pred) - Returns the position of the first element of the last subrange in the range [beg1,end1) that matches all of the elements in the range [beg2,end2) - Returns end1 if no match found STL Algorithms 7 ForwardIterator1 find_first_of(ForwardIterator1 beg1, ForwardIterator1 end1, ForwardIterator2 beg2, ForwardIterator2 end2) (Binary Pred) - Returns the postion of the first element in the range [beg1,end1) that is also in the range [beg2,end2) - Returns end1 if no match found bool equal(InputIterator1 beg1, InputIterator1 end1, InputIterator2 beg2) (Binary Pred) - Returns whether or not the elements in the range [beg1,end1) are equal to the corresponding elements in the range [beg2,end2) STL Algorithms 8 pair<InputIterator1, InputIterator2> mismatch(InputIterator1 beg1, InputIterator1 end1, InputIterator2 beg2) (Binary Pred) - Returns the first positions in the ranges starting with beg1 and beg2 that are different - Returns end1 if no mismatch found Mutating Algorithms Note: Associative containers cannot be used as the destination of any of the following algorithms (because of the underlying sort of these containers) STL Algorithms 8 OutputIterator copy(InputIterator sourceBeg, InputIterator sourceEnd, OutputIterator destBeg) - Copies the range [sourceBeg,sourceEnd) to the range starting at position destBeg, overwriting the elements already there (no memory is allocated, unless insert iterators are used) - The source and destination may overlap - Returns the position in the destination after the last element copied STL Algorithms 9 BidirectionalIterator copy_backward(InputIterator sourceBeg, InputIterator sourceEnd, BidirectionalIterator destEnd) - Copies the range [sourceBeg,sourceEnd) to the range starting at position destEnd (moving backwards through the destination, so that the elements end up in the same order as the source). Elements are overwritten (no memory is allocated, unless insert iterators are used) - The source and destination may overlap - Returns the position in destination before the last element copied STL Algorithms 10 OutputIterator transform(InputIterator sourceBeg, InputIterator sourceEnd, OutputIterator destBeg, Function f) - Applies the function f to each element of source range and copies the return value of the function to the destination (no memory is allocated, unless insert iterators are used) - The source and destination ranges may be the same - Returns the position after the last element copied STL Algorithms 11 OutputIterator transform(InputIterator1 source1Beg, InputIterator1 source1End, InputIterator2 source2Beg, OutputIterator destBeg, BinaryFunction f) - Applies the binary function f to the corresponding elements of the two source ranges and copies the return value to the destination (no memory is allocated, unless insert iterators are used) - The source and destination ranges may be the same - Returns the position after the last element copied STL Algorithms 12 ForwardIterator2 swap_ranges(ForwardIterator1 beg1, ForwardIterator1 end1, ForwardIterator2 beg2) - Swaps the elements in the ranges beginning with beg1 and beg2 - Returns the position after the last swapped element in second range void fill(ForwardIterator beg, ForwardIterator end, const T &val) - Assigns val to the elements in the range [beg,end) STL Algorithms 13 void fill_n(OutputIterator pos, Size n, const T &val) - Assigns val to the first n elements beginning at position pos - Destination must be large enough (else insert iterators must be used) void generate(ForwardIterator beg, ForwardIterator end, Function f) - Assigns the output of function f to the elements in range [beg,end) - Note: the elements are not used as input to f (as in the transform algorithm) STL Algorithms 14 void generate_n(OutputIterator pos, Size n, Function f) - Assigns the output of f to the n elements beginning at position pos void replace(ForwardIterator beg, ForwardIterator end, const T &old_val, const T &new_val) - Replaces each instance of old_val with new_val in the range [beg,end) STL Algorithms 15 void replace_if(ForwardIterator beg, ForwardIterator end, Predicate pred, const T &new_val) - Replaces each element in the range [beg,end) which makes pred true with new_val void replace_copy(ForwardIterator beg, ForwardIterator end, OutputIterator destBeg, const T &old_val, const T &new_val) - Copies source range to destination range, in the process it replaces each instance of old_val with new_val in the destination range STL Algorithms 16 void replace_copy_if(ForwardIterator beg, ForwardIterator end, OutputIterator destBeg, Predicate pred, const T &new_val) - Copies source range to destination range, in the process it replaces each element that makes pred true with new_val in the destination range STL Algorithms 17 Removing Algorithms ForwardIterator remove(ForwardIterator beg, ForwardIterator end, const T &val) - "Removes" all elements in the range equal to val - The "removed" elements are moved to positions after return position. - The function returns the position after the last element not removed STL Algorithms 18 ForwardIterator remove_if(ForwardIterator beg, ForwardIterator end, Predicate pred) - "Removes" all elements in the range for which predicate is true - The "removed" elements are moved to positions after return position. - The function returns the position after the last element not removed STL Algorithms 19 OutputIterator remove_copy(InputIterator sourceBeg, InputIterator sourceEnd, OutputIterator destBeg, const T &val) - Copies source range to destination range, in the process not copying any elements in the source range equal to val. - Destination must be large enough (else insert iterators must be used) - Returns position after last copied element in the destination STL Algorithms 20 OutputIterator remove_copy_if(InputIterator sourceBeg, InputIterator sourceEnd, OutputIterator destBeg, Predicate pred) - Copies source range to destination range, in the process not copying any elements in the source range which make predicate true - Destination must be large enough (else insert iterators must be used) - Returns position after last copied element in the destination STL Algorithms 21 ForwardIterator unique(ForwardIterator beg, ForwardIterator end) (Binary Pred) - "Removes" the second of consecutive equal elements in the range [beg,end) - The "removed" elements are moved to positions after return position. - The function returns the position after the last element not removed STL Algorithms 22 OutputIterator unique_copy(InputIterator sourceBeg, InputIterator sourceEnd, OutputIterator destBeg) (Binary Pred) - Copies source range to destination range, in the process not copying consecutive duplicates. - Destination must be large enough (else insert iterators must be used) - Returns position after last copied element in the destination STL Algorithms 23 Reordering Algorithms void reverse(BidirectionalIterator beg, BidirectionalIterator end) - Reverses the elements in the range [beg,end) STL Algorithms 24 void rotate(ForwardIterator beg, ForwardIterator newBeg, ForwardIterator end) - Rotates elements in range [beg,end) so that the element that was in position newBeg is now in position beg STL Algorithms 25 OutputIterator rotate_copy(ForwardIterator beg, ForwardIterator newBeg, ForwardIterator end, OutputIterator destBeg) - Copies the elements from source range to the destination, in the process rotating the elements so that the element in position newBeg ends up in position destBeg - Destination must be large enough (else insert iterators must be used) - Returns position after last copied element in the destination STL Algorithms 26 bool next_permutation(Bidirectional beg, Bidirectional end) - Permutes the elements in range [beg,end) using the next permutation of the elements in the lexicographical ordering of all permutations. - If such a permutation exists it returns true, otherwise it sorts the sequence in ascending order and returns false STL Algorithms 27 bool prev_permutation(Bidirectional beg, Bidirectional end) - Permutes the elements in range [beg,end) using the previous permutation of the elements in the lexicographical ordering of all permutations. - If such a permutation exists it returns true, otherwise it sorts the sequence in descending order and returns false STL Algorithms 28 void random_shuffle(RandomAccessIterator beg, RandomAccessIterator end) void random_shuffle(RandomAccessIterator beg, RandomAccessIterator end, RandomFunc f) - Shuffles the elements in the range [beg,end), in the first case using the standard library random number generator, and in the second case using a user-supplied random number generator f. STL Algorithms 29 BidirectionalIterator partition(BidirectionalIterator beg, BidirectionalIterator end, Predicate pred) BidirectionalIterator stable_partition(BidirectionalIterator beg, BidirectionalIterator end, Predicate pred) - Moves all elements in the range [beg,end) for which predicate is true up to the beginning of the range - The second function preserves the relative order of the matched and the unmatched elements. STL Algorithms 30 Sorting Algorithms void sort(RandomAccessIterator beg, RandomAccessIterator end) (Binary Pred) void stable_sort(RandomAccessIterator beg, RandomAccessIterator end) (Binary Pred) - Sorts the elements in the range [beg,end) - The second function maintains the relative order of equal elements STL Algorithms 31 void partial_sort(RandomAccessIterator beg, RandomAccessIterator sortEnd, RandomAccessIterator end) (Bin Pred) - After this function the elements in the range [beg,sortEnd) are in the correct order that they would be in if the entire range [beg,end) were sorted STL Algorithms 32 RandomAccessIterator partial_sort_copy(InputIterator beg1, InputIterator end1, RandomAccessIterator beg2, RandomAccessIterator end2) (Binary Pred) - This takes the elements in range [beg1,end1) and copies them in sorted order to the range [beg2,end2) - The number of elements that are sorted and copied is the minimum of the source and destination ranges - Returns position after last copied element in the destination STL Algorithms 33 void nth_element(RandomAccessIterator beg, RandomAccessIterator nth, RandomAccessIterator end) (Bin Pred) - Rearranges range [beg,end) so that the correct element is in position n (in sorted order) and the elements to the right are greater or equal and the elements to the left are less than or equal STL Algorithms 34 Sorted Range Algorithms All of the following algorithms assume that the range is already sorted bool binary_search(ForwardIterator beg, ForwardIterator end, const T &val) (Binary Pred) - Returns whether or not val is in the range [beg,end) STL Algorithms 35 bool includes(InputIterator beg1, InputIterator end1, InputIterator beg2, InputIterator end2) (Binary Pred) - Returns whether or not the sorted range [beg2,end2) is found exactly as is in the sorted range [beg1,end1) STL Algorithms 36 ForwardIter lower_bound(ForwardIter beg, ForwardIter end, const T &val) (BinaryPred) ForwardIter upper_bound(ForwardIter beg, ForwardIter end, const T &val) (Binary Pred) - lower_bound() returns the position of the first occurrence of val - upper_bound() returns the position of the first element greater than val STL Algorithms 37 pair<ForwardIter,ForwardIter> equal_range(ForwardIter beg, ForwardIter end, const T &val) (Binary Pred) - Returns iterators to the first element equal and the first element greater than val STL Algorithms 38 OutputIterator merge(InputIterator1 source1Beg, InputIterator1 source1End, InputIterator2 source2Beg, InputIterator source2End, OutputIterator destBeg) (Binary Pred) - Merge the two source ranges and copy it to the destination range - Destination must be large enough (else insert iterators must be used) - Returns position after last copied element in the destination STL Algorithms 39 Numeric Algorithms T accumulate(InputIterator beg, InputIterator end, T initVal) T accumulate(InputIterator beg, InputIterator end, T initVal, BinaryOp op) - Returns the sum of initVal plus all the elements in range [beg,end) STL Algorithms 40 OutputIterator partial_sum(InputIterator sourceBeg, InputIterator sourceEnd, OutputIterator destBeg) (Binary Pred) - Calculates the partial sums for the elements in source range and copies them over to the destination range - If x1, x2, x3, ... are the source values then x1, x1+x2, x1+x2+x3, ... are the values copied to the destination STL Algorithms 41 Set Operations template <class InputIterator1, class InputIterator2, class OutputIterator> OutputIterator set_union ( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result ); (Bin Pred) - Constructs a sorted range beginning in the location pointed by result with the set union of the two sorted ranges [first1,last1) and [first2,last2) as content. STL Algorithms 42 template <class InputIterator1, class InputIterator2, class OutputIterator> OutputIterator set_difference ( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result ) (Bin Pred) - Constructs a sorted range beginning in the location pointed by result with the set difference of range [first1,last1) with respect to [first2,last2) as content. STL Algorithms 43 template <class InputIterator1, class InputIterator2, class OutputIterator> OutputIterator set_symmetric_difference ( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result ) (Bin Pred) - Constructs a sorted range beginning in the location pointed by result with the set symmetric difference of the two sorted ranges [first1,last1) and [first2,last2) as content. STL Algorithms 44 template <class InputIterator1, class InputIterator2, class OutputIterator> OutputIterator set_intersection ( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result ) (Bin Pred) - Constructs a sorted range beginning in the location pointed by result with the set intersection of the two sorted ranges [first1,last1) and [first2,last2) as content.