The interesting part is that the kind of data that can be stored inside these containers has been left unspecified at the time the containers were constructed. That's why they are spoken of as abstract containers.
Abstract containers rely heavily on templates, covered in chapter
21 and beyond. To use abstract containers, only a minimal grasp of
the template concept is required. In C++ a template is in fact a recipe
for constructing a function or a complete class. The recipe tries to abstract
the functionality of the class or function as much as possible from the data
on which the class or function operates. As the data types on which the
templates operate were not known when the template was implemented, the
datatypes are either inferred from the context in which a function template is
used, or they are mentioned explicitly when a class template is used (the term
that's used here is instantiated). In situations where the types are
explicitly mentioned, the angle bracket notation is used to indicate
which data types are required. For example, below (in section 12.2) we'll
encounter the pair
container, which requires the
explicit mentioning of two data types. Here is a pair
object
containing both an int
and a string
:
pair<int, string> myPair;
The object myPair
is defined as an object holding both an int
and
a string
.
The angle bracket notation is used intensively in the upcoming discussion of abstract containers. Actually, understanding this part of templates is the only real requirement for using abstract containers. Now that we've introduced this notation, we can postpone the more thorough discussion of templates to chapter 21, and concentrate on their use in this chapter.
Most abstract containers are sequential containers: they contain
data that can be stored and retrieved in some sequential
way. Examples are the array
, implementing a fixed-sized array; a
vector
, implementing an extendable array; the
list
, implementing a data structure that allows for the easy insertion or
deletion of data; the queue
, also called a FIFO
(first in, first out) structure, in which the first element that is
entered is the first element to be retrieved again; and the stack
,
which is a
first in, last out (FILO or LIFO) structure.
In addition to sequential containers several special containers are
available. The pair
is a basic container in which a pair of values (of
types that are left open for further specification) can be stored, like two
strings, two ints, a string and a double, etc.. Pairs are often used to return
data elements that naturally come in pairs. For example, the map
is an
abstract container storing keys and their associated values. Elements
of these maps are returned as pairs
.
A variant of the pair
is the complex
container, implementing
operations that are defined on complex numbers.
A tuple
(cf. section 22.6) generalizes the pair
container to a
data structure accommodating any number of different data types.
All abstract containers described in this chapter as well as the string
and stream datatypes (cf. chapters 5 and 6) are part of
the Standard Template Library.
All but the unordered containers support the following basic set of operators:
==
and !=
The equality operator applied to two containers returns true
if the two
containers have the same number of elements, which are pairwise equal
according to the equality operator of the contained data type. The
inequality operator does the opposite;
<
, <=
, >
and
>=
. The <
operator returns true
if each element in the
left-hand side container is less than each corresponding element in the
right-hand side container. Additional elements in either the left-hand side
container or the right-hand side container are ignored.
container left; container right; left = {0, 2, 4}; right = {1, 3}; // left < right right = {1, 3, 6, 1, 2}; // left < right
class
-type) can be stored in a container, the
user-defined type should at least support:
Sequential containers can also be initialized using initializer lists.
Most containers (exceptions are the stack (section 12.4.11), priority_queue (section 12.4.5), and queue (section 12.4.4) containers) support members to determine their maximum sizes (through their member function max_size).
Virtually all containers support copy construction. If the container supports copy construction and the container's data type supports move construction, then move construction is automatically used for the container's data elements when a container is initialized with an anonymous temporary container.
Closely linked to the standard template library are the
generic algorithms. These algorithms may be used to perform
frequently occurring tasks or more complex tasks than is possible with the
containers themselves, like counting, filling, merging, filtering etc.. An
overview of generic algorithms and their applications is given in chapter
19. Generic algorithms usually rely on the availability of
iterators, representing begin and
end-points for processing data stored inside containers. The abstract
containers usually support constructors and members expecting iterators, and
they often have members returning iterators (comparable to the
string::begin
and string::end
members). In this chapter the
iterator concept is not further investigated. Refer to chapter 18 for
this.
Containers often collect data during their lifetimes. When a container
goes out of scope, its destructor tries to destroy its data elements. This
only succeeds if the data elements themselves are stored inside the
container. If the data elements of containers
are pointers to dynamically allocated memory then the memory pointed to by
these pointers is not destroyed, resulting in a memory leak. A consequence
of this scheme is that the data stored in a container should often be
considered the `property' of the container: the container should be able to
destroy its data elements when the container's destructor is called. So,
normally containers should not contain pointers to data. Also, a container
should not be required
to contain const
data, as const
data
prevent the use of many of the container's members, like the assignment
operator.
std::
is usually omitted.
pair
may represent pair<string,
int>
.
Type
represents the generic type. Type
could
be int
, string
, etc.
object
and container
represent objects of the
container type under discussion.
value
represents a value of the type that is
stored in the container.
n
represent unsigned
values.
pos
, from
, beyond
map
container, contain pairs of
values, usually called `keys' and `values'. For such containers the following
notational convention is used in addition:
key
indicates a value of the used key-type
keyvalue
indicates a value of the `value_type
'
used with the particular container.
pair
container is a rather basic container. It is
used to store two elements, called first
and second
, and that's about
it. Before using pair
containers the header file <utility>
must be
included.
The pair
's data types are specified when the pair
object is
defined (or declared) using the template's angle bracket notation (cf. chapter
21). Examples:
pair<string, string> piper("PA28", "PH-ANI"); pair<string, string> cessna("C172", "PH-ANG");
here, the variables piper
and cessna
are defined as pair
variables containing two strings
. Both strings can be retrieved using the
first
and second
fields of the pair
type:
cout << piper.first << '\n' << // shows 'PA28' cessna.second << '\n'; // shows 'PH-ANG'
The first
and second
members can also be used to reassign values:
cessna.first = "C152"; cessna.second = "PH-ANW";
If a pair
object must be completely reassigned, an anonymous
pair object can be used as the right-hand operand of
the assignment. An anonymous variable defines a temporary variable (which
receives no name) solely for the purpose of (re)assigning another variable of
the same type. Its generic form is
type(initializer list)
Note that when a pair
object is used the type specification is not
completed by just mentioning the containername pair
. It also requires the
specification of the data types which are stored within the pair. For this the
(template) angle bracket notation is used again. E.g., the reassignment
of the cessna
pair variable could have been accomplished as follows:
cessna = pair<string, string>("C152", "PH-ANW");
In cases like these, the type
specification can become quite
elaborate, in which case using
declarations can be used to improve
readability. If many pair<type1, type2>
clauses are
used in a source, the typing effort may be reduced and readability might be
improved by first defining a name for the clause, and then using the defined
name later. E.g.,
using pairStrStr = pair<string, string>; cessna = pairStrStr("C152", "PH-ANW");
All abstract containers are class templates, and the types for which class templates are initialized are commonly specified between pointed brackets following the class template's name. However, the compiler may be able to deduce the container's types from the types of arguments that are specified when constructing the container. E.g., when defining
pair values{ 1, 1.5 };
the compiler deduces that values.first
is an int
and
values.second
is a double
. Sometimes the class template's types cannot
be deduced. In those cases the intended types must explicitly be specified:
pair<int, double> values;
Although the compiler will deduce types whenever it can, it might not deduce the types we had in mind. Had we defined
pair cessna{ "C172", "PH-BVL" };
then the compilation would succeed, but an expression like
cout << cessna.first.length()
would not compile, as "C172"
is
a NTBS, and hence cessna.first
is a char *
. In this case simply
appending an s
to the NTBSs fixes the problem, but such a simple fix might
not always be available. Section 12.4.2 has contains more information
about deducing template parameter types.
Apart from this (and the basic set of operations (assignment and
comparisons)) the pair
offers no further functionality. It is, however,
a basic ingredient of the upcoming abstract containers map, multimap
and
hash_map
.
C++ also offers a generalized pair container: the tuple, covered in section 22.6.
get_allocator
member, which returns
a copy of the allocator used by the container. Allocators offer the following
members:
value_type *address(value_type &object)
returns the address of object
.
value_type *allocate(size_t count)
allocates raw memory for holdingcount
values of the container'svalue_type
.
void construct(value_type *object, Arg &&...args)
using placement new, uses the arguments followingobject
to install a value atobject
.
void destroy(value_type *object)
callsobject
's destructor (but doesn't deallocateobject
's own memory).
void deallocate(value_type *object, size_t count)
callsoperator delete
to delete object's memory, previously allocated byallocate
.
size_t max_size()
returns the maximum number of elements that allocate
can
allocate.
Here is an example, using the allocator of a vector of strings (see
section 12.4.2 below for a description of the vector
container):
#include <iostream> #include <vector> #include <string> using namespace std; int main() { vector<string> vs; auto allocator = vs.get_allocator(); // get the allocator string *sp = allocator.allocate(3); // alloc. space for 3 strings allocator.construct(&sp[0], "hello world"); // initialize 1st string allocator.construct(&sp[1], sp[0]); // use the copy constructor allocator.construct(&sp[2], 12, '='); // string of 12 = chars cout << sp[0] << '\n' << // show the strings sp[1] << '\n' << sp[2] << '\n' << "could have allocated " << allocator.max_size() << " strings\n"; for (size_t idx = 0; idx != 3; ++idx) allocator.destroy(sp + idx); // delete the string's // contents allocator.deallocate(sp, 3); // and delete sp itself again. }
array
class implements a
fixed-size array. Before using the array
container the <array>
header file must be included.
To define a std::array
both the data type of its elements and its size
must be specified: the data type is given after an opening angle bracket,
immediately following the `array
' container name. The array's size is
provided after the data type specification. Finally, a closing angle bracket
completes the array's type. Specifications like this are common practice with
containers. The combination of array
, type and size defines a
type. As a result, array<string, 4>
defines another type than
array<string, 5>
, and a function explicitly defining an array<Type, N>
parameter will not accept an array<Type, M>
argument if N
and M
are unequal.
The array's size may may be defined as 0 (although such an array probably has
little use as it cannot store any element). The elements of an array are
stored contiguously. If array<Type, N> arr
has been defined, then
&arr[n] + m == &arr[n + m]
, assuming than 0 <= n < N
and 0 <= n + m
< N
.
The following constructors, operators, and member functions are available:
array
may be constructed with a fixed number N
of
default elements:
array<string, N> object;
array<double, 4> dArr = {1.2, 2.4};
Here dArr
is defined as an array of 4 element, with dArr[0]
and
dArr[1]
initialized to, respectively 1.2 and 2.4, and dArr[2]
and
dArr[3]
initialized to 0. An attractive characteristic of arrays (and
other containers) is that containers initialize
their data elements to the data type's default value. The data type's
default constructor is used for this initialization. With non-class
data types the value 0 is used. So, for an array<double, 4>
array we know
that all but its explicitly initialized elements are initialized to
zero.
array
supports the index operator, which can be used to retrieve or reassign
individual elements of the array. Note that the elements which are indexed
must exist. For example, having defined an empty array a statement like
iarr[0] = 18
produces an error, as the array is empty. Note that
operator[]
does not respect its
array bounds. If you want run-time array bound checking, use the array's
at
member.
array
class offers the following
member functions:
Type &at(size_t idx)
:idx
. If idx
exceeds the array's size a
std::out_of_range
exception is thrown.
Type &back()
:array::iterator begin()
:end
if the array is empty.
array::const_iterator cbegin()
:cend
if the array is empty.
array::const_iterator cend()
:array::const_reverse_iterator crbegin()
:crend
if the array is empty.
array::const_reverse_iterator crend()
:value_type *data()
:value_type const *
is returned.
bool empty()
:true
if the array contains no elements.
array::iterator end()
:void fill(Type const &item)
:item
Type &front()
:array::reverse_iterator rbegin()
:array::reverse_iterator rend()
:constexpr size_t size()
:void swap(<array<Type, N> &other)
:swap
.
array
rather than a standard C style array offers several
advantages:
size
can be used);
array
container can be used in the context of templates,
there code is developed that operates on data types that become
available only after the code itself has been developed;
array
supports reverse iterators, it can be immediately be
used with generic algorithms performing `reversed' operations (e.g.,
to perform a descending rather than ascending sort (cf. section
19.1.59))
array
or
vector
(introduced in the next section) should be your `weapon of
choice'. Only if these containers demonstrably do not fit the problem at hand
you should use another type of container.
vector
class implements an
expandable array. Before using the vector
container the <vector>
header file must be included.
The following constructors, operators, and member functions are available:
vector
may be constructed empty:
vector<string> object;
vector<string> object(5, "Hello"s); // initialize to 5 Hello's, vector<string> container(10); // and to 10 empty strings vector<string> names = {"george", "frank", "tony", "karel"};
Note the difference between vector<int> first(5)
and vector<int>
second{ 5 }
. The vector first
contains five elements, initialized to 0,
while the vector second
contains one element, initialized to 5. Referring
back to section 12.2: with the latter definition the compiler is able to
deduce
the vector's template parameter
type (int
), so the latter definition could also have been written as
vector second{ 5 }
.
An ambiguity might be observed when looking at
vector object{ vector{ 1 } };
Did we define a vector<int>
or a vector<vector<int>>
? The standard
considers this a vector<int>
: it is initialized using the vector's move
constructor from an abstract vector<int>
.
vector<string>
the following construction may be used:
extern vector<string> container; vector<string> object(&container[5], &container[11]);
Note here that the last element pointed to by the second iterator
(&container[11]
) is not stored in object
. This is a simple
example of the use of iterators, in which the used
range of values starts at the first value, and includes all elements up
to but not including the element to which the second iterator refers. The
standard notation for this is [begin, end)
.
vector
supports the index operator, which can be used to retrieve or reassign
individual elements of the vector. Note that the elements which are indexed
must exist. For example, having defined an empty vector a statement like
ivect[0] = 18
produces an error, as the vector is empty. So, the vector
is not automatically
expanded, and operator[]
does not respect its
array bounds. In this case the vector should be resized first, or
ivect.push_back(18)
should be used (see below). If you need run-time array
bound checking, use the vector's at
member.
vector
class offers the following
member functions:
void assign(...)
:assign(iterator begin, iterator end)
assigns the values at
the iterator range [begin, end)
to the vector;
assign(size_type n, value_type const &val)
assigns n
copies of val
to the vector;
assign(initializer_list<value_type> values)
assigns the
values in the initializer list to the vector.
Type &at(size_t idx)
:idx
. If idx
exceeds the vector's size a
std::out_of_range
exception is thrown.
Type &back()
:vector::iterator begin()
:end
if the vector is empty.
size_t capacity()
:size
vector::const_iterator cbegin()
:cend
if the vector is empty.
vector::const_iterator cend()
:void clear()
:vector::const_reverse_iterator crbegin()
:crend
if the vector is empty.
vector::const_reverse_iterator crend()
:value_type *data()
:iterator emplace(const_iterator position, Args &&...args)
:value_type
object is constructed from the arguments
specified after position
, and the newly created element is inserted at
position
. Different from insert
, which expects an existing object of
the container's value type (and inserts
the provided argument into the container) using copy or move construction or
assignment, emplace
uses its arguments to
construct such an object immediately at the intended location of the
container, without requiring copy or move construction or assignment.
void emplace_back(Args &&...args)
:value_type
object is constructed from the member's
arguments, and the newly created element is inserted beyond the vector's last
element.
bool empty()
:true
if the vector contains no
elements.
vector::iterator end()
:vector::iterator erase()
:erase(pos)
erases the element pointed to by the iterator
pos
. The iterator ++pos
is returned.
erase(first, beyond)
erases elements indicated by the iterator
range [first, beyond)
, returning beyond
.
Type &front()
:allocator_type get_allocator() const
:vector
object.
... insert()
:insert()
that is called:
vector::iterator insert(pos)
inserts a default value of type
Type
at pos
, pos
is returned.
vector::iterator insert(pos, value)
inserts value
at
pos
, pos
is returned.
void insert(pos, first, beyond)
inserts the elements in the
iterator range [first, beyond)
.
void insert(pos, n, value)
inserts n
elements having value
value
at position pos
.
size_t max_size()
:vector
may contain.
void pop_back()
:size()
member to return the (when cast to int
) value -1, and
then -2, etc., etc.
void push_back(value)
:value
to the end of the vector.
vector::reverse_iterator rbegin()
:vector::reverse_iterator rend()
:void reserve(size_t request)
:request
is less than or equal to capacity
, this
call has no effect. Otherwise, it is a request to allocate additional
memory. If the call is successful, then capacity
returns a value of at
least request
. Otherwise, capacity
is unchanged. In either case,
size
's return value won't change, until a function like resize
is
called, actually changing the number of accessible elements.
void resize()
:resize(n, value)
may be used to resize the vector to a size
of n
. Value
is optional. If the vector is expanded and value
is
not provided, the additional elements are initialized to the default value
of the used data type, otherwise value
is used to initialize extra
elements.
void shrink_to_fit()
:vector<Type>(vectorObject).swap(vectorObject)
idiom can be used.
size_t size()
:void swap()
:#include <iostream> #include <vector> using namespace std; int main() { vector<int> v1(7); vector<int> v2(10); v1.swap(v2); cout << v1.size() << " " << v2.size() << '\n'; } /* Produced output: 10 7 */
list
container implements a list data structure.
Before using a list
container the header file <list>
must be included.
The organization of a list
is shown in figure 10.
As a subtlety note that the representation given in figure 10 is not necessarily used in actual implementations of the list. For example, consider the following little program:
int main() { list<int> l; cout << "size: " << l.size() << ", first element: " << l.front() << '\n'; }
When this program is run it might actually produce the output:
size: 0, first element: 0
Its front element can even be assigned a value. In this case the implementor has chosen to provide the list with a hidden element. The list actually is a circular list, where the hidden element serves as terminating element, replacing the 0-pointers in figure 10. As noted, this is a subtlety, which doesn't affect the conceptual notion of a list as a data structure ending in 0-pointers. Note also that it is well known that various implementations of list-structures are possible (cf. Aho, A.V., Hopcroft J.E. and Ullman, J.D., (1983) Data Structures and Algorithms (Addison-Wesley)).
Both lists and vectors are often appropriate data structures in situations where an unknown number of data elements must be stored. However, there are some rules of thumb to follow when selecting the appropriate data structure.
vector
is the preferred data
structure. Example: in a program counting character frequencies in
a textfile, a vector<int> frequencies(256)
is the
datastructure of choice, as the values of the received characters
can be used as indices into the frequencies
vector.
vector
: if the number of elements is known in
advance (and does not notably change during the lifetime of the
program), the vector is also preferred over the list.
vector
should be the preferred container; even when
implementing algorithms traditionally using lists.
Other considerations related to the choice between lists and vectors should also be given some thought. Although it is true that the vector is able to grow dynamically, the dynamic growth requires data-copying. Clearly, copying a million large data structures takes a considerable amount of time, even on fast computers. On the other hand, inserting a large number of elements in a list doesn't require us to copy non-involved data. Inserting a new element in a list merely requires us to juggle some pointers. In figure 11 this is shown: a new element is inserted between the second and third element, creating a new list of four elements.
Removing an element from a list is also fairly easy. Starting again from the situation shown in figure 10, figure 12 shows what happens if element two is removed from our list. Again: only pointers need to be juggled. In this case it's even simpler than adding an element: only two pointers need to be rerouted. To summarize the comparison between lists and vectors: it's probably best to conclude that there is no clear-cut answer to the question what data structure to prefer. There are rules of thumb, which may be adhered to. But if worse comes to worst, a profiler may be required to find out what's best.
The list
container offers the following constructors, operators, and
member functions:
list
may be constructed empty:
list<string> object;
As with the vector
, it is an error to refer to an element of an
empty list.
list<string> object(5, "Hello"s); // initialize to 5 Hello's list<string> container(10); // and to 10 empty strings
vector<string>
the following construction may be used:
extern vector<string> container; list<string> object(&container[5], &container[11]);
list
does not offer specialized operators, apart from the
standard operators for containers.
void assign(...)
:
assigns new content to the list:
assign(iterator begin, iterator end)
assigns the values at
the iterator range [begin, end)
to the list;
assign(size_type n, value_type const &val)
assigns n
copies of val
to the list;
Type &back()
:list::iterator begin()
:end
if the list is
empty.
void clear()
:bool empty()
:true
if the list contains no
elements.
list::iterator end()
:list::iterator erase()
:erase(pos)
erases the element pointed to by pos
. The
iterator ++pos
is returned.
erase(first, beyond)
erases elements indicated by the iterator
range [first, beyond)
. Beyond
is returned.
Type &front()
:allocator_type get_allocator() const
:list
object.
... insert()
:insert
that is called:
list::iterator insert(pos)
inserts a default value of type
Type
at pos
, pos
is returned.
list::iterator insert(pos, value)
inserts value
at
pos
, pos
is returned.
void insert(pos, first, beyond)
inserts the elements in the
iterator range [first, beyond)
.
void insert(pos, n, value)
inserts n
elements having value
value
at position pos
.
size_t max_size()
:list
may contain.
void merge(list<Type> other)
:sort
). Based on that assumption, it inserts the
elements of other
into the current list in such a way that the
modified list remains sorted. If both list are not sorted, the
resulting list will be ordered `as much as possible', given the
initial ordering of the elements in the two
lists. list<Type>::merge
uses Type::operator<
to sort the
data in the list, which operator must therefore be available. The
next example illustrates the use of the merge
member: the list
`object
' is not sorted, so the resulting list is ordered 'as
much as possible'.
#include <iostream> #include <string> #include <list> using namespace std; void showlist(list<string> &target) { for ( list<string>::iterator from = target.begin(); from != target.end(); ++from ) cout << *from << " "; cout << '\n'; } int main() { list<string> first; list<string> second; first.push_back("alpha"s); first.push_back("bravo"s); first.push_back("golf"s); first.push_back("quebec"s); second.push_back("oscar"s); second.push_back("mike"s); second.push_back("november"s); second.push_back("zulu"s); first.merge(second); showlist(first); }A subtlety is that
merge
doesn't alter the list if the list
itself is used as argument: object.merge(object)
won't change
the list `object
'.
void pop_back()
:void pop_front()
:void push_back(value)
:value
to the end of
the list.
void push_front(value)
:value
before the
first element of the list.
list::reverse_iterator rbegin()
:void remove(value)
:value
from the list. In the following example, the two strings
`Hello
' are removed from the list object
:
#include <iostream> #include <string> #include <list> using namespace std; int main() { list<string> object; object.push_back("Hello"s); object.push_back("World"s); object.push_back("Hello"s); object.push_back("World"s); object.remove("Hello"s); while (object.size()) { cout << object.front() << '\n'; object.pop_front(); } } /* Generated output: World World */
void remove_if(Predicate pred)
:pred
returns true
. For each of the objects
stored in the list the predicate is called as pred(*iter)
,
where iter
represents the iterator used internally by
remove_if
. If a function pred
is used, its prototype
should be bool pred(value_type const &object)
.
list::reverse_iterator rend()
:void resize()
:void reverse()
:back
becomes front
and vice
versa.
size_t size()
:void sort()
:unique
member function
below. list<Type>::sort
uses Type::operator<
to sort the
data in the list, which must therefore be available.
void splice(pos, object)
:object
to the current list, starting the insertion at the
iterator position pos
of the object using the splice
member. Following splice
, object
is empty. For example:
#include <iostream> #include <string> #include <list> using namespace std; int main() { list<string> object; object.push_front("Hello"s); object.push_back("World"s); list<string> argument(object); object.splice(++object.begin(), argument); cout << "Object contains " << object.size() << " elements, " << "Argument contains " << argument.size() << " elements,\n"; while (object.size()) { cout << object.front() << '\n'; object.pop_front(); } }Alternatively,
argument
may be followed by an iterator of
argument
, indicating the first element of argument
that
should be spliced, or by two iterators begin
and end
defining the iterator-range [begin, end)
on argument
that should be spliced into object
.
void swap()
:void unique()
:list<Type>::unique
uses Type::operator==
to identify
identical data elements, which operator must therefore be
available. Here's an example removing all multiply occurring
words from the list: #include <iostream> #include <string> #include <list> using namespace std; // see the merge() example void showlist(list<string> &target) { for ( list<string>::iterator from = target.begin(); from != target.end(); ++from ) cout << *from << " "; cout << '\n'; } int main() { string array[] = { "charlie", "alpha", "bravo", "alpha" }; list<string> target ( array, array + sizeof(array) / sizeof(string) ); cout << "Initially we have:\n"; showlist(target); target.sort(); cout << "After sort() we have:\n"; showlist(target); target.unique(); cout << "After unique() we have:\n"; showlist(target); } /* Generated output: Initially we have: charlie alpha bravo alpha After sort() we have: alpha alpha bravo charlie After unique() we have: alpha bravo charlie */
queue
class implements a queue data structure. Before using a
queue
container the header file <queue>
must be included.
A queue is depicted in figure 13.
In figure 13 it is shown that a queue has one point (the back) where items can be added to the queue, and one point (the front) where items can be removed (read) from the queue. Aqueue
is therefore
also called a FIFO data structure, for first in, first out. It is
most often used in situations where events should be handled in the same order
as they are generated.
The following constructors, operators, and member functions are available
for the queue
container:
queue
may be constructed empty:
queue<string> object;
As with the vector
, it is an error to refer to an element of an
empty queue.
queue
container only supports the basic container operators.
Type &back()
:
bool empty()
:true
if the queue contains no elements.
Type &front()
:void pop()
:size()
member to return the (when cast to int
)
value -1, and then -2, etc., etc. One might wonder why
pop
returns void
, instead of a value of type Type
(cf. front
). One reason is found in the principles of good software
design: functions should perform one task. Combining the removal and return of
the removed element breaks this principle. Moreover, when this principle is
abandoned pop
's implementation is always flawed. Consider the
prototypical implementation of a pop
member that is supposed to return the
just popped value:
Type queue::pop() { Type ret{ front() }; erase_front(); return ret; }
The venom, as usual, is in the tail: since queue
has no control over
Type
's behavior the final statement (return ret
) might throw. By that
time the queue's front element has already been removed from the queue and
so it is lost. Thus, a Type
returning pop
member cannot offer the
strong guarantee and consequently pop
should not return the former
front
element. Because of all this, we must first use front
and
then pop
to obtain and remove the queue's front element.
void push(value)
:value
to the back of the queue.
size_t size()
:priority_queue
class implements a priority queue data structure.
Before using a priority_queue
container the <queue>
header file must
have been included.
A priority queue is identical to a queue
, but allows the entry of data
elements according to priority rules. A real-life priority queue is
found, e.g., at airport check-in terminals. At a terminal the passengers
normally stand in line to wait for their turn to check in, but late passengers
are usually allowed to jump the queue: they receive a higher priority than
other passengers.
The priority queue uses operator<
of the data type stored in the
priority queue to decide about the priority of the data elements. The
smaller the value, the lower the priority. So, the priority queue
could be used to sort values while they arrive. A simple example of such
a priority queue application is the following program: it reads words from
cin
and writes a sorted list of words to cout
:
#include <iostream> #include <string> #include <queue> using namespace std; int main() { priority_queue<string> q; string word; while (cin >> word) q.push(word); while (q.size()) { cout << q.top() << '\n'; q.pop(); } }
Unfortunately, the words are listed in reversed order: because of the
underlying <
-operator the words appearing later in the ASCII-sequence
appear first in the priority queue. A solution to that problem is to define a
wrapper class around the string
datatype, reversing string
's
operator<
. Here is the modified program:
#include <iostream> #include <string> #include <queue> class Text { std::string d_s; public: Text(std::string const &str) : d_s(str) {} operator std::string const &() const { return d_s; } bool operator<(Text const &right) const { return d_s > right.d_s; } }; using namespace std; int main() { priority_queue<Text> q; string word; while (cin >> word) q.push(word); while (q.size()) { word = q.top(); cout << word << '\n'; q.pop(); } }
Other possibilities to achieve the same exist. One would be to store the content of the priority queue in, e.g., a vector, from which the elements can be read in reversed order.
The following constructors, operators, and member functions are available
for the priority_queue
container:
priority_queue
may be constructed empty:
priority_queue<string> object;
priority_queue
only supports the basic operators of
containers.
bool empty()
:true
if the
priority queue contains no elements.
void pop()
:queue
avoid calling this member on an empty priority queue (cf. section
12.4.4.)
void push(value)
:value
at the
appropriate position in the priority queue.
size_t size()
:Type &top()
:Note that the priority queue does not support iterators or an index operator. The only element that can be accessed is its top element. A priority queue can be emptied by:
deque
(pronounce: `deck') class implements a
doubly ended queue data structure (deque). Before using a deque
container the header file <deque>
must be included.
A deque
is comparable to a queue, but it allows for reading and
writing at both ends. Actually, the deque
data type supports a lot more
functionality than the queue
, as illustrated by the following overview of
available member functions. A deque
is a combination of a vector
and
two queues, operating at both ends of the vector. In situations where random
insertions and the addition and/or removal of elements at one or both sides of
the vector occurs frequently using a deque
should be considered.
The following constructors, operators, and member functions are available for deques:
deque
may be constructed empty:
deque<string> object;
As with the vector
, it is an error to refer to an element of an
empty deque.
deque<string> object(5, "Hello"s), // initialize to 5 Hello's deque<string> container(10); // and to 10 empty strings
vector<string>
the following construction may be used:
extern vector<string> container; deque<string> object(&container[5], &container[11]);
void assign(...)
:
assigns new content to the deque:
assign(iterator begin, iterator end)
assigns the values at
the iterator range [begin, end)
to the deque;
assign(size_type n, value_type const &val)
assigns n
copies of val
to the deque;
Type &at(size_t idx)
:
returns a reference to the deque's element at index positionidx
. Ifidx
exceeds the deque's size astd::out_of_range
exception is thrown.
Type &back()
:deque::iterator begin()
:deque::const_iterator cbegin()
:
returns a const_iterator pointing to the first
element in the deque, returning cend
if the deque is empty.
deque::const_iterator cend()
:
returns a const_iterator pointing just beyond the deque's last element.
void clear()
:deque::const_reverse_iterator crbegin()
:
returns a const_reverse_iterator pointing to the last
element in the deque, returning crend
if the deque is empty.
deque::const_reverse_iterator crend()
:
returns a const_reverse_iterator pointing just before the deque's first element.
iterator emplace(const_iterator position, Args &&...args)
avalue_type
object is constructed from the arguments specified afterposition
, and the newly created element is inserted atposition
.
void emplace_back(Args &&...args)
a value_type
object is constructed from the member's
arguments, and the newly created element is inserted beyond the deque's last
element.
void emplace_front(Args &&...args)
a value_type
object is constructed from the member's
arguments, and the newly created element is inserted before the deque's first
element.
bool empty()
:true
if the deque contains no elements.
deque::iterator end()
:deque::iterator erase()
:erase(pos)
erases the element pointed to by pos
. The
iterator ++pos
is returned.
erase(first, beyond)
erases elements indicated by the iterator
range [first, beyond)
. Beyond
is returned.
Type &front()
:allocator_type get_allocator() const
:deque
object.
... insert()
:insert
that is
called:
deque::iterator insert(pos)
inserts a default value of type
Type
at pos
, pos
is returned.
deque::iterator insert(pos, value)
inserts value
at
pos
, pos
is returned.
void insert(pos, first, beyond)
inserts the elements in the
iterator range [first, beyond)
.
void insert(pos, n, value)
inserts n
elements having value
value
starting at iterator position pos
.
size_t max_size()
:deque
may contain.
void pop_back()
:size()
member to return the (when cast to
int
) value -1, and then -2, etc., etc.
void pop_front()
:pop_back()
its
internally maintained count of number of available elements is reduced
void push_back(value)
:value
to the end of
the deque.
void push_front(value)
:value
before the
first element of the deque.
deque::reverse_iterator rbegin()
:deque::reverse_iterator rend()
:void resize()
:resize(n, value)
may be used to resize the deque to a size of
n
. Value
is optional. If the deque is expanded and value
is not
provided, the additional elements are initialized to the default value of the
used data type, otherwise value
is used to initialize extra elements.
void shrink_to_fit()
:deque<Type>(dequeObject).swap(dequeObject)
idiom
can be used.
size_t size()
:void swap(argument)
:
map
class offers a (sorted) associative array. Before using a
map
container the <map>
header file must be included.
A map
is filled with key, value pairs, which may be of any
container-accepted type. Since types are associated with both the key and the
value, we must specify two types in the angle bracket notation, comparable
to the specification we've seen with the pair
container (cf. section
12.2). The first type represents the key's type, the second type
represents the value's type. For example, a map
in which the key is a
string
and the value is a double
can be defined as follows:
map<string, double> object;
The key is used to access its associated information. That
information is called the value. For example, a phone book uses the names
of people as the key, and uses the telephone number and maybe other
information (e.g., the zip-code, the address, the profession) as value. Since
a map
sorts its keys, the key
's operator<
must be defined, and it
must be sensible to use it. For example, it is generally a bad idea to use
pointers for keys, as sorting pointers is something different than sorting the
values pointed at by those pointers. In addition to the key, value types,
a third type defines the comparison class, used to compare two keys. By
default, the comparison class is std::less<KeyType
(cf. section
18.1.2), using the key type's operator<
to compare two key
values. For key type KeyType
and value type ValueType
the map's type
definition, therefore, looks like this:
map<KeyType, ValueType, std::less<KeyType>>
The two fundamental operations on maps are the storage of key, value
combinations, and the retrieval of values, given their keys. The index
operator using a key as the index, can be used for both. If the index operator
is used as lvalue, the expression's rvalue
is inserted into the
map. If it is used as rvalue, the key's associated value is retrieved.
Each key can be stored only once in a map
. If the same key is entered
again, the new value replaces the formerly stored value, which is lost.
A specific key, value combination can implicitly or explicitly be inserted
into a map
. If explicit insertion is required, the key, value
combination must be constructed first. For this, every map
defines a
value_type
which may be used to create values that can be stored in the
map
. For example, a value for a map<string, int>
can be constructed as
follows:
map<string, int>::value_type siValue{ "Hello", 1 };
The value_type
is associated with the map<string, int>
: the
type of the key is string
, the type of the value is int
. Anonymous
value_type
objects are also often used. E.g.,
map<string, int>::value_type{ "Hello", 1 };
Instead of using the line map<string, int>::value_type(...)
over and
over again, a using declaration is frequently used to reduce typing and to
improve readability:
using StringIntValue = map<string, int>::value_type;
Now values for the map<string, int>
may be specified this way:
StringIntValue{ "Hello", 1 };
Alternatively, pairs
may be used to represent key, value
combinations used by maps:
pair<string, int>{ "Hello", 1 };
map
container:
map
may be constructed empty:
map<string, int> object;
Note that the values stored in maps may be containers themselves. For
example, the following defines a map
in which the value is a pair
: a
container
nested under another container:
map<string, pair<string, string>> object;
Note the use of the two consecutive closing angle brackets, which does not result in ambiguities as their syntactical context differs from their use as binary operators in expressions.
value_type
values for the map to be constructed, or to
plain pair
objects. If pairs are used, their first
element represents
the type of the keys, and their second
element represents the type of the
values. Example:
pair<string, int> pa[] = { pair<string,int>("one", 1), pair<string,int>("two", 2), pair<string,int>("three", 3), }; map<string, int> object(&pa[0], &pa[3]);
In this example, map<string, int>::value_type
could have been written
instead of pair<string, int>
as well.
If begin
represents the first iterator that is used to construct a map
and if end
represents the second iterator, [begin, end)
will be
used to initialize the map. Maybe contrary to intuition, the map
constructor only enters new keys. If the last element of pa
would
have been "one", 3
, only two elements would have entered the map
:
"one", 1
and "two", 2
. The value "one", 3
would silently have been
ignored.
The map
receives its own copies of the data to which the iterators
point as illustrated by the following example:
#include <iostream> #include <map> using namespace std; class MyClass { public: MyClass() { cout << "MyClass constructor\n"; } MyClass(MyClass const &other) { cout << "MyClass copy constructor\n"; } ~MyClass() { cout << "MyClass destructor\n"; } }; int main() { pair<string, MyClass> pairs[] = { pair<string, MyClass>{ "one", MyClass{} } }; cout << "pairs constructed\n"; map<string, MyClass> mapsm{ &pairs[0], &pairs[1] }; cout << "mapsm constructed\n"; } /* Generated output: MyClass constructor MyClass copy constructor MyClass destructor pairs constructed MyClass copy constructor mapsm constructed MyClass destructor MyClass destructor */
When tracing the output of this program, we see that, first, the
constructor of a MyClass
object is called to initialize the anonymous
element of the array pairs
. This object is then copied into the first
element of the array pairs
by the copy constructor. Next, the original
element is not required anymore and is destroyed. At that point the array
pairs
has been constructed. Thereupon, the map
constructs a temporary
pair
object, which is used to construct the map element. Having
constructed the map element, the temporary pair
object is
destroyed. Eventually, when the program terminates, the pair
element
stored in the map
is destroyed too.
The index operator may be used to retrieve or reassign individual elements of the map. The argument of the index operator is called a key.
If the provided key is not available in the map
, a new data element is
automatically added to the map
using the default value or default
constructor to initialize the value part of the new element. This default
value is returned if the index operator is used as an rvalue.
When initializing a new or reassigning another element of the map, the type of
the right-hand side of the assignment operator must be equal to (or promotable
to) the type of the map's value part. E.g., to add or change the value of
element "two"
in a map, the following statement can be used:
mapsm["two"] = MyClass{};
map
container:
mapped_type &at(key_type const &key)
:mapped_type
associated with key
. If the key is not stored in the map
an
std::out_of_range
exception is thrown.
map::iterator begin()
:map::const_iterator cbegin()
:cend
if the map is empty.
map::const_iterator cend()
:void clear()
:size_t count(key)
:map
, otherwise 0 is returned.
map::reverse_iterator crbegin() const
:map::reverse_iterator crend()
:pair<iterator, bool> emplace(Args &&...args)
:value_type
object is constructed from emplace
's
arguments. If the map
already contained an object using the same
key_type
value, then a std::pair
is returned containing an iterator
pointing to the object using the same key_type
value and the value
false
. If no such key_type
value was found, the newly constructed
object is inserted into the map
, and the returned std::pair
contains an iterator pointing to the newly inserted value_type
as well as the value true
.
iterator emplace_hint(const_iterator position,
Args &&...args)
:value_type
object is constructed from the member's
arguments, and the newly created element is inserted into the
map
, unless the (at args
) provided key already exists. The
implementation may or may not use position
as a hint to start looking
for an insertion point. The returned iterator
points to the value_type
using the provided key. It may refer to an already existing value_type
or
to a newly added value_type
; an existing value_type
is not
replaced. If a new value was added, then the container's size has been
incremented when emplace_hint
returns.
bool empty()
:true
if
the map contains no elements.
map::iterator end()
:pair<map::iterator, map::iterator> equal_range(key)
:lower_bound
and upper_bound
, introduced below.
An example illustrating these member functions is given at the
discussion of the member function upper_bound
.
... erase()
:bool erase(key)
erases the element having the
given key
from the map
. True
is returned if the value was removed,
false
if the map did not contain an element using the given key
.
void erase(pos)
erases the element pointed to by the iterator
pos
.
void erase(first, beyond)
erases all elements indicated by
the iterator range [first, beyond)
.
map::iterator find(key)
:end
is returned. The following example illustrates the use of
the find
member function:
#include <iostream> #include <map> using namespace std; int main() { map<string, int> object; object["one"] = 1; map<string, int>::iterator it = object.find("one"); cout << "`one' " << (it == object.end() ? "not " : "") << "found\n"; it = object.find("three"); cout << "`three' " << (it == object.end() ? "not " : "") << "found\n"; } /* Generated output: `one' found `three' not found */
allocator_type get_allocator() const
:map
object.
... insert()
:insert
that is called:
pair<map::iterator, bool> insert(keyvalue)
inserts
a new value_type
into the map. The return value is a
pair<map::iterator, bool>
. If the returned bool
field is true
,
keyvalue
was inserted into the map. The value false
indicates that the
key that was specified in keyvalue
was already available in the map, and
so keyvalue
was not inserted into the map. In both cases the
map::iterator
field points to the data element having the
key
that was specified in keyvalue
. The use of this variant of
insert
is illustrated by the following example:
#include <iostream> #include <string> #include <map> using namespace std; int main() { pair<string, int> pa[] = { pair<string,int>("one", 10), pair<string,int>("two", 20), pair<string,int>("three", 30), }; map<string, int> object(&pa[0], &pa[3]); // {four, 40} and `true' is returned pair<map<string, int>::iterator, bool> ret = object.insert ( map<string, int>::value_type ("four", 40) ); cout << boolalpha; cout << ret.first->first << " " << ret.first->second << " " << ret.second << " " << object["four"] << '\n'; // {four, 40} and `false' is returned ret = object.insert ( map<string, int>::value_type ("four", 0) ); cout << ret.first->first << " " << ret.first->second << " " << ret.second << " " << object["four"] << '\n'; } /* Generated output: four 40 true 40 four 40 false 40 */Note the somewhat peculiar constructions like
cout << ret.first->first << " " << ret.first->second << ...
Note that `ret
' is equal to the pair
returned by the
insert
member function. Its `first
' field is an iterator into the
map<string, int>
, so it can be considered a pointer to a map<string,
int>::value_type
. These value types themselves are pairs too, having
`first
' and `second
' fields. Consequently, `ret.first->first
' is
the key of the map value (a string
), and `ret.first->second
' is
the value (an int
).
map::iterator insert(pos, keyvalue)
. This way a
map::value_type
may also be inserted into the map. pos
is ignored, and
an iterator to the inserted element is returned.
void insert(first, beyond)
inserts the (map::value_type
)
elements pointed to by the iterator range [first, beyond)
. Values that
were already present are not replaced.
key_compare key_comp()
:map
to compare keys. The
type map<KeyType, ValueType>::key_compare
is defined by the map
container and key_compare
's parameters have types KeyType const
&
. The comparison function returns true
if the first key
argument should be ordered before the second key argument. To compare
keys and values, use value_comp
, listed below.
map::iterator lower_bound(key)
:keyvalue
element of which the key
is at least equal to the specified
key
. If no such element exists, the function returns end
.
size_t max_size()
:map
may contain.
map::reverse_iterator rbegin()
:map::reverse_iterator rend()
:size_t size()
:void swap(argument)
:map::iterator upper_bound(key)
:keyvalue
element having a key
exceeding the specified key
. If no
such element exists, the function returns end
. The following
example illustrates the member functions equal_range
, lower_bound
and upper_bound
:
#include <iostream> #include <map> using namespace std; int main() { pair<string, int> pa[] = { pair<string,int>("one", 10), pair<string,int>("two", 20), pair<string,int>("three", 30), }; map<string, int> object(&pa[0], &pa[3]); map<string, int>::iterator it; if ((it = object.lower_bound("tw")) != object.end()) cout << "lower-bound `tw' is available, it is: " << it->first << '\n'; if (object.lower_bound("twoo") == object.end()) cout << "lower-bound `twoo' not available" << '\n'; cout << "lower-bound two: " << object.lower_bound("two")->first << " is available\n"; if ((it = object.upper_bound("tw")) != object.end()) cout << "upper-bound `tw' is available, it is: " << it->first << '\n'; if (object.upper_bound("twoo") == object.end()) cout << "upper-bound `twoo' not available" << '\n'; if (object.upper_bound("two") == object.end()) cout << "upper-bound `two' not available" << '\n'; pair < map<string, int>::iterator, map<string, int>::iterator > p = object.equal_range("two"); cout << "equal range: `first' points to " << p.first->first << ", `second' is " << ( p.second == object.end() ? "not available" : p.second->first ) << '\n'; } /* Generated output: lower-bound `tw' is available, it is: two lower-bound `twoo' not available lower-bound two: two is available upper-bound `tw' is available, it is: two upper-bound `twoo' not available upper-bound `two' not available equal range: `first' points to two, `second' is not available */
value_compare value_comp()
:map
to compare keys. The
type map<KeyType, ValueType>::value_compare
is defined by the map
container and value_compare
's parameters have types value_type
const &
. The comparison function returns true
if the first
key argument should be ordered before the second key argument. The
Value_Type
elements of the value_type
objects passed to this
member are not used by the returned function.
map
represents
a sorted associative array. In a map
the keys are sorted. If an
application must visit all elements in a map the begin
and end
iterators must be used.
The following example illustrates how to make a simple table listing all keys and values found in a map:
#include <iostream> #include <iomanip> #include <map> using namespace std; int main() { pair<string, int> pa[] = { pair<string,int>("one", 10), pair<string,int>("two", 20), pair<string,int>("three", 30), }; map<string, int> object(&pa[0], &pa[3]); for ( map<string, int>::iterator it = object.begin(); it != object.end(); ++it ) cout << setw(5) << it->first.c_str() << setw(5) << it->second << '\n'; } /* Generated output: one 10 three 30 two 20 */
map
, the multimap
class implements a (sorted)
associative array. Before using a multimap
container the header
file <map>
must be included.
The main difference between the map
and the multimap
is that the
multimap supports multiple values associated with the same key, whereas the
map contains single-valued keys. Note that the multimap also accepts multiple
identical values associated with identical keys.
The map
and the multimap
have the same set of constructors and member
functions, with the exception of the index operator which is not supported
with the multimap. This is understandable: if
multiple entries of the same key are allowed, which of the possible values
should be returned for object[key]
?
Refer to section 12.4.7 for an overview of
the multimap
member functions. Some member functions, however, deserve
additional attention when used in the context of the multimap
container. These members are discussed below.
size_t map::count(key)
:key
.
... erase()
:size_t erase(key)
erases all elements having the
given key
. The number of erased elements is returned.
void erase(pos)
erases the single element pointed to by
pos
. Other elements possibly having the same keys are not erased.
void erase(first, beyond)
erases all elements indicated by
the iterator range [first, beyond)
.
pair<multimap::iterator, multimap::iterator> equal_range(key)
:lower_bound
and upper_bound
, introduced below. The function provides
a simple means to determine all elements in the multimap
that have the
same keys
. An example illustrating the use of these member functions is
given at the end of this section.
multimap::iterator find(key)
:key
. If the element isn't available, end
is returned. The
iterator could be incremented to visit all elements having the same key
until it is either end
, or the iterator's first
member is
not equal to key
anymore.
multimap::iterator insert()
:pair<multimap::iterator, bool>
as returned with the
map
container. The returned iterator points to the newly added element.
lower_bound
and upper_bound
act
identically in the map
and multimap
containers, their operation in a
multimap
deserves some additional attention. The next example illustrates
lower_bound
, upper_bound
and
equal_range
applied to a multimap
:
#include <iostream> #include <map> using namespace std; int main() { pair<string, int> pa[] = { pair<string,int>("alpha", 1), pair<string,int>("bravo", 2), pair<string,int>("charlie", 3), pair<string,int>("bravo", 6), // unordered `bravo' values pair<string,int>("delta", 5), pair<string,int>("bravo", 4), }; multimap<string, int> object(&pa[0], &pa[6]); using msiIterator = multimap<string, int>::iterator; msiIterator it = object.lower_bound("brava"); cout << "Lower bound for `brava': " << it->first << ", " << it->second << '\n'; it = object.upper_bound("bravu"); cout << "Upper bound for `bravu': " << it->first << ", " << it->second << '\n'; pair<msiIterator, msiIterator> itPair = object.equal_range("bravo"); cout << "Equal range for `bravo':\n"; for (it = itPair.first; it != itPair.second; ++it) cout << it->first << ", " << it->second << '\n'; cout << "Upper bound: " << it->first << ", " << it->second << '\n'; cout << "Equal range for `brav':\n"; itPair = object.equal_range("brav"); for (it = itPair.first; it != itPair.second; ++it) cout << it->first << ", " << it->second << '\n'; cout << "Upper bound: " << it->first << ", " << it->second << '\n'; } /* Generated output: Lower bound for `brava': bravo, 2 Upper bound for `bravu': charlie, 3 Equal range for `bravo': bravo, 2 bravo, 6 bravo, 4 Upper bound: charlie, 3 Equal range for `brav': Upper bound: bravo, 2 */In particular note the following characteristics:
lower_bound
and upper_bound
produce the same result for
non-existing keys: they both return the first element having a key that
exceeds the provided key.
multimap
, the values for
equal keys are not ordered: they are retrieved in the order in which they were
entered.
set
class implements a sorted collection of values. Before using
set
containers the <set>
header file must be included.
A set contains unique values (of a container-acceptable type). Each value is stored only once.
A specific value can be explicitly created: Every set
defines a
value_type
which may be used to create values that can be stored in the
set
. For example, a value for a set<string>
can be constructed as
follows:
set<string>::value_type setValue{ "Hello" };
Like the std::map
container, the std::set
also has an additional
parameter declaring the class that is used for comparing values in the set.
For value type ValueType
the set's type definition, therefore, looks like
this:
set<ValueType, std::less<ValueType>>
The value_type
is associated with the set<string>
. Anonymous
value_type
objects are also often used. E.g.,
set<string>::value_type{ "Hello" };
Instead of using the line set<string>::value_type(...)
over
and over again, a using declaration is often used to reduce typing and to
improve readability:
using StringSetValue = set<string>::value_type;
Now values for the set<string>
may be constructed as follows:
StringSetValue{ "Hello" };
Alternatively, values of the set's type may be used
immediately. In that case the value of type Type
is implicitly
converted to a set<Type>::value_type
.
The following constructors, operators, and member functions are available
for the set
container:
set
may be constructed empty:
set<int> object;
int intarr[] = {1, 2, 3, 4, 5}; set<int> object{ &intarr[0], &intarr[5] };
Note that all values in the set must be different: it is not possible to store the same value repeatedly when the set is constructed. If the same value occurs repeatedly, only the first instance of the value is entered into the set; the remaining values are silently ignored.
Like the map, the set
receives its own copy of the data it
contains.
set
container only supports the standard set of operators
that are available for containers.
set
class has the following member functions:
set::iterator begin()
:end
is returned.
void clear()
:size_t count(value)
:set
, otherwise 0 is returned.
bool empty()
:true
if
the set contains no elements.
set::iterator end()
:pair<set::iterator, set::iterator> equal_range(value)
:lower_bound
and upper_bound
, introduced
below.
... erase()
:bool erase(value)
erases the element having the given
value
from the set
. True
is returned if the value was removed,
false
if the set did not contain an element `value
'.
void erase(pos)
erases the element pointed to by the iterator
pos
.
void erase(first, beyond)
erases all elements
indicated by the iterator range [first, beyond)
.
set::iterator find(value)
:end
is returned.
allocator_type get_allocator() const
:set
object.
... insert()
:set
. If the
element already exists, the existing element is left untouched and the element
to be inserted is ignored. The return value depends on the version of
insert
that is called:
pair<set::iterator, bool> insert(value)
inserts
a new set::value_type
into the set. The return value is a
pair<set::iterator, bool>
. If the returned bool
field is true
,
value
was inserted into the set. The value false
indicates that the
value that was specified was already available in the set, and
so the provided value
was not inserted into the set. In both cases the
set::iterator
field points to the data element in the set
having the
specified value
.
set::iterator insert(pos, value)
. This way a
set::value_type
may also be inserted into the set. pos
is ignored, and
an iterator to the inserted element is returned.
void insert(first, beyond)
inserts the (set::value_type
)
elements pointed to by the iterator range [first, beyond)
into the
set. Values that were already present are not replaced.
key_compare key_comp()
:set
to compare keys. The
type set<ValueType>::key_compare
is defined by the set container
and key_compare
's parameters have types ValueType const &
. The
comparison function returns true
if its first argument should
be ordered before its second argument.
set::iterator lower_bound(value)
:value
element of
which the value
is at least equal to the specified value
. If no
such element exists, the function returns end
.
size_t max_size()
:set
may contain.
set::reverse_iterator rbegin()
:set::reverse_iterator rend
:size_t size()
:void swap(argument)
:argument
being
the second set) that use identical data types.
set::iterator upper_bound(value)
:value
element having a value
exceeding the specified value
. If no
such element exists, the function returns end
.
value_compare value_comp()
:set
to compare values. The
type set<ValueType>::value_compare
is defined by the set container and
value_compare
's parameters have types ValueType const &
. The
comparison function returns true
if its first argument should be
ordered before its second argument. Its operation is identical to that
of a key_compare
object, returned by key_comp
.
set
, the multiset
class implements a
sorted collection of values. Before
using multiset
containers the header file <set>
must be included.
The main difference between the set
and the multiset
is that the
multiset
supports multiple entries of the same value, whereas the set
contains unique values.
The set
and the multiset
have the same set of constructors and member
functions. Refer to section 12.4.9 for an overview of the member functions
that can be used with the multiset
. Some member functions, however,
behave slightly different than their counterparts of the set
container. Those members are:
size_t count(value)
:value
.
... erase()
:size_t erase(value)
erases all elements having the given
value
. The number of erased elements is returned.
void erase(pos)
erases the element pointed to by the iterator
pos
. Other elements possibly having the same values are not erased.
void erase(first, beyond)
erases all elements indicated by
the iterator range [first, beyond)
.
pair<multiset::iterator, multiset::iterator> equal_range(value)
:lower_bound
and upper_bound
, introduced below. The function provides
a simple means to determine all elements in the multiset
that have the
same values
.
multiset::iterator find(value)
:end
is returned. The iterator could be
incremented to visit all elements having the given value
until it is
either end
, or the iterator doesn't point to `value
' anymore.
... insert()
:pair<multiset::iterator,
bool>
as returned with the set
container. The returned iterator points to
the newly added element.
lower_bound
and upper_bound
act identically
in the set
and multiset
containers, their operation in a multiset
deserves some additional attention. With a multiset
container
lower_bound
and upper_bound
produce the same result for non-existing
keys: they both return the first element having a key exceeding the provided
key.
Here is an example showing the use of various member functions of a multiset:
#include <iostream> #include <set> using namespace std; int main() { string sa[] = { "alpha", "echo", "hotel", "mike", "romeo" }; multiset<string> object(&sa[0], &sa[5]); object.insert("echo"); object.insert("echo"); multiset<string>::iterator it = object.find("echo"); for (; it != object.end(); ++it) cout << *it << " "; cout << '\n'; cout << "Multiset::equal_range(\"ech\")\n"; pair < multiset<string>::iterator, multiset<string>::iterator > itpair = object.equal_range("ech"); if (itpair.first != object.end()) cout << "lower_bound() points at " << *itpair.first << '\n'; for (; itpair.first != itpair.second; ++itpair.first) cout << *itpair.first << " "; cout << '\n' << object.count("ech") << " occurrences of 'ech'" << '\n'; cout << "Multiset::equal_range(\"echo\")\n"; itpair = object.equal_range("echo"); for (; itpair.first != itpair.second; ++itpair.first) cout << *itpair.first << " "; cout << '\n' << object.count("echo") << " occurrences of 'echo'" << '\n'; cout << "Multiset::equal_range(\"echoo\")\n"; itpair = object.equal_range("echoo"); for (; itpair.first != itpair.second; ++itpair.first) cout << *itpair.first << " "; cout << '\n' << object.count("echoo") << " occurrences of 'echoo'" << '\n'; } /* Generated output: echo echo echo hotel mike romeo Multiset::equal_range("ech") lower_bound() points at echo 0 occurrences of 'ech' Multiset::equal_range("echo") echo echo echo 3 occurrences of 'echo' Multiset::equal_range("echoo") 0 occurrences of 'echoo' */
stack
class implements a stack data structure. Before using
stack
containers the header file <stack>
must be included.
A stack is also called a first in, last out (FILO or LIFO) data structure as the first item to enter the stack is the last item to leave. A stack is an extremely useful data structure in situations where data must temporarily remain available. For example, programs maintain a stack to store local variables of functions: the lifetime of these variables is determined by the time these functions are active, contrary to global (or static local) variables, which live for as long as the program itself lives. Another example is found in calculators using the Reverse Polish Notation (RPN), in which the operands of operators are kept in a stack, whereas operators pop their operands off the stack and push the results of their work back onto the stack.
As an example of the use of a stack, consider figure 14, in which
the content of the stack is shown while the expression (3 + 4) * 2
is
evaluated. In the RPN this expression becomes 3 4 + 2 *
, and figure
14 shows the stack content after each token (i.e., the
operands and the operators) is read from the input. Notice that each operand
is indeed pushed on the stack, while each operator changes the content of the
stack.
3
) is now at
the bottom of the stack. Next, in step 3, the +
operator is read. The
operator pops two operands (so that the stack is empty at that moment),
calculates their sum, and pushes the resulting value (7
) on the
stack. Then, in step 4, the number 2
is read, which is dutifully pushed on
the stack again. Finally, in step 5 the final operator *
is read, which
pops the values 2
and 7
from the stack, computes their product, and
pushes the result back on the stack. This result (14
) could then be popped
to be displayed on some medium.
From figure 14 we see that a stack has one location (the top) where items can be pushed onto and popped off the stack. This top element is the stack's only immediately visible element. It may be accessed and modified directly.
Bearing this model of the stack in mind, let's see what we formally can do
with the stack
container. For the stack
, the following constructors,
operators, and member functions are available:
stack
may be constructed empty:
stack<string> object;
stack
bool empty()
:true
if the stack contains no elements.
void pop()
:pop
has
return type void
and why pop
should not be called on an empty
stack
.
void push(value)
:value
at the top of the stack, hiding the other elements from view.
size_t size()
:Type &top()
:The stack does not support iterators or an index operator. The only elements that can be accessed is its top element. To empty a stack:
unordered_map
.
Before using unordered_map
or unordered_multimap
containers the header
file <unordered_map>
must be included.
The unordered_map
class implements an associative array in which the
elements are stored according to some hashing scheme. As discussed, the
map is a sorted data structure. The keys in maps are sorted using the
operator<
of the key's data type. Generally, this is not the fastest way
to either store or retrieve data. The main benefit of sorting is that a
listing of sorted keys appeals more to humans than an unsorted list. However,
a by far faster way to store and retrieve data is to use hashing.
Hashing uses a function (called the hash function) to compute an (unsigned) number from the key, which number is thereupon used as an index in the table storing the keys and their values. This number is called the bucket number. Retrieval of a key is as simple as computing the hash value of the provided key, and looking in the table at the computed index location: if the key is present, it is stored in the table, at the computed bucket location and its value can be returned. If it's not present, the key is not currently stored in the container.
Collisions occur when a computed index position is already
occupied by another element. For these situations the abstract containers have
solutions available. A simple solution, used by unordered_maps
, consists
of using linear chaining, which uses linked list to store colliding table
elements.
The term unordered_map is used rather than hash to avoid name collisions with hash tables developed before they were added to the language.
Because of the hashing method, the efficiency of a unordered_map
in
terms of speed should greatly exceed the efficiency of the map
. Comparable
conclusions may be drawn for the unordered_set
, the unordered_multimap
and the unordered_multiset
.
unordered_map
type five template arguments must be
specified :
unordered_map::key_type
),
unordered_map::mapped_type
),
unordered_map::hasher
), and
unordered_map::key_equal
).
The generic definition of an unordered_map
container looks like this:
std::unordered_map <KeyType, ValueType, hash type, predicate type, allocator type>
When KeyType
is std::string
or a built-in type then default types
are available for the hash type and the predicate type. In practice the
allocator type is not specified, as the default allocator suffices. In these
cases an unordered_map
object can be defined by merely specifying the key-
and value types, like this:
std::unordered_map<std::string, ValueType> hash(size_t size = implSize);
Here, implSize
is the container's default initial size, which is
specified by the implementor. The map's size is automatically enlarged by the
unordered_map
when necessary, in which case the container rehashes all
its elements. In practice the default size
argument provided by the
implementor is completely satisfactory.
The KeyType
frequently consists of text. So, a unordered_map
using a
std::string KeyType
is frequently used. Be careful not to use a plain
char const * key_type
as two char const *
values pointing to equal
C-strings stored at different locations are considered to be different
keys, as their pointer values rather than their textual content are
compared. Here is an example showing how a char const * KeyType
can be
used. Note that in the example no arguments are specified when constructing
months
, since default values and constructors are available:
#include <unordered_map> #include <iostream> #include <string> #include <cstring> using namespace std; struct EqualCp { bool operator()(char const *l, char const *r) const { return strcmp(l, r) == 0; } }; struct HashCp { size_t operator()(char const *str) const { return std::hash<std::string>()(str); } }; int main() { unordered_map<char const *, int, HashCp, EqualCp> months; // or explicitly: unordered_map<char const *, int, HashCp, EqualCp> monthsTwo(61, HashCp(), EqualCp()); months["april"] = 30; months["november"] = 31; string apr("april"); // different pointers, same string cout << "april -> " << months["april"] << '\n' << "april -> " << months[apr.c_str()] << '\n'; }
If other KeyTypes
must be used, then the unordered_map
's constructor
requires (constant references to) a hash function object, computing a hash
value from a key value, and a predicate function object, returning true
if
two unordered_map::key_type
objects are identical. A generic
algorithm (see chapter 19) exists performing tests of equality
(i.e., equal_to
). These tests can be used if the key's data type supports
the equality operator. Alternatively, an overloaded operator==
or
specialized function object could be constructed returning true
if two
keys are equal and false
otherwise.
Constructors
The unordered_map
supports the following constructors:
explicit unordered_map(size_type n = implSize,
hasher const &hf = hasher(),
key_equal const &eql = key_equal(),
allocator_type const &alloc = allocator_type())
:
this constructor can also be used as default constructor;
unordered_map(const_iterator begin,
const_iterator end,
size_type n = implSize, hasher const &hf = hasher(),
key_equal const &eql = key_equal(),
allocator_type const &alloc = allocator_type())
:
this constructor expects two iterators specifying
a range of unordered_map::value_type const
objects, and
unordered_map(initializer_list<value_type> initList,
size_type n = implSize, hasher const &hf = hasher(),
key_equal const &eql = key_equal(),
allocator_type const &alloc = allocator_type())
:
a constructor expecting an initializer_list
of
unordered_map::value_type
values.
The following example shows a program using an unordered_map containing
the names of the months of the year and the number of days these months
(usually) have. Then, using the subscript operator the days in several months
are displayed (the predicate used here is the generic algorithm
equal_to<string>
, which is provided by the compiler as the default fourth
argument of the unordered_map
constructor):
#include <unordered_map> #include <iostream> #include <string> using namespace std; int main() { unordered_map<string, int> months; months["january"] = 31; months["february"] = 28; months["march"] = 31; months["april"] = 30; months["may"] = 31; months["june"] = 30; months["july"] = 31; months["august"] = 31; months["september"] = 30; months["october"] = 31; months["november"] = 30; months["december"] = 31; cout << "september -> " << months["september"] << '\n' << "april -> " << months["april"] << '\n' << "june -> " << months["june"] << '\n' << "november -> " << months["november"] << '\n'; } /* Generated output: september -> 30 april -> 30 june -> 30 november -> 30 */
unordered_map
supports the index operator operating identically to the
map
's index operator: a (const) reference to the ValueType
associated
with the provided KeyType
's value is returned. If not yet available, the
key is added to the unordered_map
, and a default ValueType
value is
returned. In addition, it supports operator==
.
The unordered_map
provides the following
member functions (key_type,
value_type
etc. refer to the types defined by the unordered_map
):
mapped_type &at(key_type const &key)
:mapped_type
associated with key
. If the key is not stored in the unordered_map
a
std::out_of_range
exception is thrown.
unordered_map::iterator begin()
:end
if the unordered_map is empty.
size_t bucket(key_type const &key)
:key
is stored. If
key
wasn't stored yet bucket
adds value_type(key, Value())
before
returning its index position.
size_t bucket_count()
:value_type
objects.
size_t bucket_size(size_t index)
:value_type
objects stored at bucket
position index
.
unordered_map::const_iterator cbegin()
:cend
if the unordered_map is empty.
unordered_map::const_iterator cend()
:void clear()
:size_t count(key_type const &key)
:value_type
object using
key_type
key
is stored in the unordered_map
(which is either one
or zero).
pair<iterator, bool> emplace(Args &&...args)
:value_type
object is constructed from emplace
's
arguments. If the unordered_map
already contained an object using the same
key_type
value, then a std::pair
is returned containing an iterator
pointing to the object using the same key_type
value and the value
false
. If no such key_type
value was found, the newly constructed
object is inserted into the unordered_map
, and the returned std::pair
contains an iterator pointing to the newly inserted inserted value_type
as well as the value true
.
iterator emplace_hint(const_iterator position,
Args &&...args)
:value_type
object is constructed from the member's
arguments, and the newly created element is inserted into the
unordered_map
, unless the (at args
) provided key already exists. The
implementation may or may not use position
as a hint to start looking
for an insertion point. The returned iterator
points to the value_type
using the provided key. It may refer to an already existing value_type
or
to a newly added value_type
; an existing value_type
is not
replaced. If a new value was added, then the container's size has been
incremented when emplace_hint
returns.
bool empty()
:true
if the unordered_map contains no elements.
unordered_map::iterator end()
:pair<iterator, iterator> equal_range(key)
:key
. With the unordered_map
this range includes
at most one element.
unordered_map::iterator erase()
:erase(pos)
erases the element pointed to by the iterator
pos
. The iterator ++pos
is returned.
erase(first, beyond)
erases elements indicated by the iterator
range [first, beyond)
, returning beyond
.
iterator find(key)
:end
is returned.
allocator_type get_allocator() const
:unordered_map
object.
hasher hash_function() const
:unordered_map
object.
... insert()
:
elements may be inserted starting at a certain position. No insertion is performed if the provided key is already in use. The return value depends on the version ofinsert()
that is called. When apair<iterator, bool>
is returned, then thepair's first
member is an iterator pointing to the element having a key that is equal to the key of the providedvalue_type
, thepair's second
member istrue
ifvalue
was actually inserted into the container, andfalse
if not.
pair<iterator, bool> insert(value_type const &value)
attempts to insert value
.
pair<iterator, bool> insert(value_type &&tmp)
attempts to insert value
using value_type
's move
constructor.
pair<iterator, bool> insert(const_iterator hint, value_type
const &value)
attempts to insert value
, possibly using hint
as a
starting point when trying to insert value
.
pair<iterator, bool> insert(const_iterator hint, value_type
&&tmp)
attempts to insert a value
using value_type
's move
constructor, and possibly using hint
as a starting point when trying to
insert value
.
void insert(first, beyond)
tries to insert the elements in
the iterator range [first, beyond)
.
void insert(initializer_list <value_type> iniList)
attempts to insert the elements in iniList
into the
container.
hasher key_eq() const
:key_equal
function object used by the unordered_map
object.
float load_factor() const
:size / bucket_count
.
size_t max_bucket_count()
:unordered_map
may contain.
float max_load_factor() const
:load_factor
.
void max_load_factor(float max)
:max
. When a load factor of max
is
reached, the container will enlarge its bucket_count
, followed by a rehash
of its elements. Note that the container's default maximum load factor equals
1.0
size_t max_size()
:unordered_map
may contain.
void rehash(size_t size)
:size
exceeds the current bucket count, then the bucket
count is increased to size
, followed by a rehash of its elements.
void reserve(size_t request)
:request
is less than or equal to the current bucket count,
this call has no effect. Otherwise, the bucket count is increased to a value
of at least request
, followed by a rehash of the container's elements.
size_t size()
:void swap(unordered_map &other)
:unordered_map
.
unordered_multimap
allows multiple objects using the same keys to be
stored in an unordered map. The unordered_multimap
container offers the
same set of members and constructors as the unordered_map
, but without the
unique-key restriction imposed upon the unordered_map
.
The unordered_multimap
does not offer operator[]
and does not offer
at
members.
Below all members are described whose behavior differs from the behavior of
the corresponding unordered_map
members:
at
not supported by the unordered_multimap
container
size_t count(key_type const &key)
:value_type
object using
key_type
key
is stored in the unordered_map
. This member is
commonly used to verify whether key
is available in the
unordered_multimap
.
iterator emplace(Args &&...args)
:value_type
object is constructed from emplace
's
arguments. The returned iterator
points to the newly inserted inserted
value_type
.
iterator emplace_hint(const_iterator position,
Args &&...args)
:value_type
object is constructed from the member's
arguments, and the newly created element is inserted into the
unordered_multimap
. The
implementation may or may not use position
as a hint to start looking
for an insertion point. The returned iterator
points to the value_type
using the provided key.
pair<iterator, iterator> equal_range(key)
:key
.
iterator find(key)
:end
is returned.
... insert()
:
elements may be inserted starting at a certain position. The return value depends on the version ofinsert()
that is called. When aniterator
is returned, then it points to the element that was inserted.
iterator insert(value_type const &value)
inserts value
.
iterator insert(value_type &&tmp)
inserts value
using value_type
's move
constructor.
iterator insert(const_iterator hint, value_type const &value)
inserts value
, possibly using hint
as a
starting point when trying to insert value
.
iterator insert(const_iterator hint, value_type &&tmp)
inserts value
using value_type
's move
constructor, and possibly using hint
as a starting point when trying to
insert value
.
void insert(first, beyond)
inserts the elements in
the iterator range [first, beyond)
.
void insert(initializer_list <value_type> iniList)
inserts the elements in iniList
into the container.
map
container, orders its elements. If
ordering is not an issue, but fast lookups are, then a hash-based set and/or
multi-set may be preferred. C++ provides such hash-based sets and
multi-sets: the unordered_set
and unordered_multiset
.
Before using these hash-based set containers the header file
<unordered_set>
must be included.
Elements stored in the unordered_set
are immutable, but they can be
inserted and removed from the container. Different from the unordered_map
,
the unordered_set
does not use a ValueType
. The set merely stores
elements, and the stored element itself is its own key.
The unordered_set
has the same constructors as the unordered_map
, but
the set's value_type
is equal to its key_type
.
When defining an unordered_set
type four template arguments must be
specified :
unordered_set::key_type
),
unordered_set::hasher
), and
unordered_set::key_equal
).
The generic definition of an unordered_set
container looks like this:
std::unordered_set <KeyType, hash type, predicate type, allocator type>
When KeyType
is std::string
or a built-in type then default types
are available for the hash type and the predicate type. In practice the
allocator type is not specified, as the default allocator suffices. In these
cases an unordered_set
object can be defined by merely specifying the key-
and value types, like this:
std::unordered_set<std::string> rawSet(size_t size = implSize);
Here, implSize
is the container's default initial size, which is
specified by the implementor. The set's size is automatically enlarged when
necessary, in which case the container rehashes all its elements. In
practice the default size
argument provided by the implementor is
completely satisfactory.
The unordered_set
supports the following constructors:
explicit unordered_set(size_type n = implSize,
hasher const &hf = hasher(),
key_equal const &eql = key_equal(),
allocator_type const &alloc = allocator_type())
:
this constructor can also be used as default constructor;
unordered_set(const_iterator begin,
const_iterator end,
size_type n = implSize, hasher const &hf = hasher(),
key_equal const &eql = key_equal(),
allocator_type const &alloc = allocator_type())
:
this constructor expects two iterators specifying
a range of unordered_set::value_type const
objects, and
unordered_set(initializer_list<value_type> initList,
size_type n = implSize, hasher const &hf = hasher(),
key_equal const &eql = key_equal(),
allocator_type const &alloc = allocator_type())
:
a constructor expecting an initializer_list
of
unordered_set::value_type
values.
The unordered_set
does not offer an index operator, and it does not offer
an at
member. Other than those, it offers the same members as the
unordered_map
. Below the members whose behavior differs from the behavior
of the unordered_map
are discussed. For a description of the remaining
members, please refer to section 12.4.12.2.
iterator emplace(Args &&...args)
:value_type
object is constructed from emplace
's
arguments. It is added to the set if it is unique, and an iterator to the
value_type
is returned.
iterator emplace_hint(const_iterator position,
Args &&...args)
:value_type
object is constructed from the member's
arguments, and if the newly created element is unique it is inserted into the
unordered_set
. The
implementation may or may not use position
as a hint to start looking
for an insertion point. The returned iterator
points to the
value_type
.
unordered_set::iterator erase()
:erase(key_type const &key)
erases key
from the set. An
iterator pointing to the next element is returned.
erase(pos)
erases the element pointed to by the iterator
pos
. The iterator ++pos
is returned.
erase(first, beyond)
erases elements indicated by the iterator
range [first, beyond)
, returning beyond
.
unordered_multiset
allows multiple objects using the same keys to be
stored in an unordered set. The unordered_multiset
container offers the
same set of members and constructors as the unordered_set
, but without the
unique-key restriction imposed upon the unordered_set
.
Below all members are described whose behavior differs from the behavior of
the corresponding unordered_set
members:
size_t count(key_type const &key)
:value_type
object using
key_type
key
is stored in the unordered_set
. This member is
commonly used to verify whether key
is available in the
unordered_multiset
.
iterator emplace(Args &&...args)
:value_type
object is constructed from emplace
's
arguments. The returned iterator
points to the newly inserted inserted
value_type
.
iterator emplace_hint(const_iterator position,
Args &&...args)
:value_type
object is constructed from the member's
arguments, and the newly created element is inserted into the
unordered_multiset
. The
implementation may or may not use position
as a hint to start looking
for an insertion point. The returned iterator
points to the value_type
using the provided key.
pair<iterator, iterator> equal_range(key)
:key
.
iterator find(key)
:end
is returned.
... insert()
:
elements may be inserted starting at a certain position. The return value depends on the version ofinsert()
that is called. When aniterator
is returned, then it points to the element that was inserted.
iterator insert(value_type const &value)
inserts value
.
iterator insert(value_type &&tmp)
inserts value
using value_type
's move
constructor.
iterator insert(const_iterator hint, value_type const &value)
inserts value
, possibly using hint
as a
starting point when trying to insert value
.
iterator insert(const_iterator hint, value_type &&tmp)
inserts value
using value_type
's move
constructor, and possibly using hint
as a starting point when trying to
insert value
.
void insert(first, beyond)
inserts the elements in
the iterator range [first, beyond)
.
void insert(initializer_list <value_type> iniList)
inserts the elements in iniList
into the container.
Since the C++14 standard arbitrary lookup key types can be used provided a
comparison operator is available to compare that type with the container's key
type. Thus, a char const *
key (or any other type for which an
operator<
overload for std::string
is available) can be used to lookup
values in a map<std::string, ValueType>
. This is called
heterogeneous lookup.
Heterogeneous lookup is allowed when the comparator given to the associative
container does allow this. The standard library classes std::less
and
std::greater
were augmented to allow heterogeneous lookup.
complex
container defines the standard operations that can be
performed on complex numbers. Before using complex
containers the
header file <complex>
must be included.
The complex number's real and imaginary types are specified as the container's data type. Examples:
complex<double> complex<int> complex<float>
Note that the real and imaginary parts of complex numbers have the same datatypes.
When initializing (or assigning) a complex object, the imaginary part may be omitted from the initialization or assignment resulting in its value being 0 (zero). By default, both parts are zero.
Below it is silently assumed that the used complex
type is
complex<double>
. Given this assumption, complex numbers may be initialized
as follows:
target
: A default initialization: real and imaginary parts are 0.
target(1)
: The real part is 1, imaginary part is 0
target(0, 3.5)
: The real part is 0, imaginary part is 3.5
target(source)
: target
is initialized with the values of
source
.
#include <iostream> #include <complex> #include <stack> using namespace std; int main() { stack<complex<double>> cstack; cstack.push(complex<double>(3.14, 2.71)); cstack.push(complex<double>(-3.14, -2.71)); while (cstack.size()) { cout << cstack.top().real() << ", " << cstack.top().imag() << "i" << '\n'; cstack.pop(); } } /* Generated output: -3.14, -2.71i 3.14, 2.71i */
The following member functions and operators are defined for complex
numbers (below, value
may be either a primitive scalar type or a
complex
object):
complex
container.
complex operator+(value)
:complex
container and value
.
complex operator-(value)
:complex
container and
value
.
complex operator*(value)
:complex
container and
value
.
complex operator/(value)
:complex
container and
value
.
complex operator+=(value)
:value
to the current complex
container, returning the
new value.
complex operator-=(value)
:value
from the current complex
container,
returning the new value.
complex operator*=(value)
:complex
container by value
, returning
the new value
complex operator/=(value)
:complex
container by value
, returning the
new value.
Type real()
:Type imag()
:complex
container, such as abs
, arg
, conj
, cos
,
cosh
, exp
, log
, norm
, polar
, pow
,
sin
, sinh
and sqrt
. All these functions are free functions,
not member functions, accepting complex numbers as their arguments. For
example,
abs(complex<double>(3, -5)); pow(target, complex<int>(2, 3));
istream
objects and inserted into ostream
objects. The insertion
results in an ordered pair (x, y)
, in which x
represents the real
part and y
the imaginary part of the complex number. The same form may
also be used when extracting a complex number from an istream
object. However, simpler forms are also allowed. E.g., when extracting
1.2345
the imaginary part is set to 0.