A number of the algorithms read, but never write to, the elements in their input range. The find
function is one such algorithm, as is the count
function we used in the exercises for § 10.1 (p. 378).
Another read-only algorithm is accumulate
, which is defined in the numeric
header. The accumulate
function takes three arguments. The first two specify a range of elements to sum. The third is an initial value for the sum. Assuming vec
is a sequence of integers, the following
// sum the elements in vec starting the summation with the value 0
int sum = accumulate(vec.cbegin(), vec.cend(), 0);
sets sum
equal to the sum of the elements in vec
, using 0
as the starting point for the summation.
The type of the third argument to accumulate
determines which addition operator is used and is the type that accumulate
returns.
The fact that accumulate
uses its third argument as the starting point for the summation has an important implication: It must be possible to add the element type to the type of the sum. That is, the elements in the sequence must match or be convertible to the type of the third argument. In this example, the elements in vec
might be int
s, or they might be double
, or long long
, or any other type that can be added to an int
.
As another example, because string
has a +
operator, we can concatenate the elements of a vector
of string
s by calling accumulate
:
string sum = accumulate(v.cbegin(), v.cend(), string(""));
This call concatenates each element in v
onto a string
that starts out as the empty string. Note that we explicitly create a string
as the third parameter. Passing the empty string as a string literal would be a compile-time error:
// error: no + on const char*
string sum = accumulate(v.cbegin(), v.cend(), "");
Had we passed a string literal, the type of the object used to hold the sum would be const char*
. That type determines which +
operator is used. Because there is no +
operator for type const char*
, this call will not compile.
Ordinarily it is best to use cbegin()
and cend()
(§ 9.2.3, p. 334) with algorithms that read, but do not write, the elements. However, if you plan to use the iterator returned by the algorithm to change an element’s value, then you need to pass begin()
and end()
.
Another read-only algorithm is equal
, which lets us determine whether two sequences hold the same values. It compares each element from the first sequence to the corresponding element in the second. It returns true
if the corresponding elements are equal, false
otherwise. The algorithm takes three iterators: The first two (as usual) denote the range of elements in the first sequence; the third denotes the first element in the second sequence:
// roster2 should have at least as many elements as roster1
equal(roster1.cbegin(), roster1.cend(), roster2.cbegin());
Because equal
operates in terms of iterators, we can call equal
to compare elements in containers of different types. Moreover, the element types also need not be the same so long as we can use ==
to compare the element types. For example, roster1
could be a vector<string>
and roster2
a list<const char*>
.
However, equal
makes one critically important assumption: It assumes that the second sequence is at least as big as the first. This algorithm potentially looks at every element in the first sequence. It assumes that there is a corresponding element for each of those elements in the second sequence.
Algorithms that take a single iterator denoting a second sequence assume that the second sequence is at least as large at the first.
Exercise 10.3: Use accumulate
to sum the elements in a vector<int>
.
Exercise 10.4: Assuming v
is a vector<double>
, what, if anything, is wrong with calling accumulate(v.cbegin(), v.cend(), 0)?
Exercise 10.5: In the call to equal
on rosters, what would happen if both rosters held C-style strings, rather than library string
s?