}
};
int main() {
SoftLogic<Noncomparable> l;
l.noOp(); }
40. Write a function template that takes a single type parameter (T) and accepts four function arguments: an array of T, a start index, a stop index (inclusive), and an optional initial value. The function returns the sum of all the array elements in the specified range. Use the default constructor of T for the default initial value.
41. Repeat the previous exercise but use explicit instantiation to manually create specializations for int and double, following the technique explained in this chapter.
42. Why does the following code not compile? (Hint: what do class member functions have access to?)
class Buddy {};
template<class T>
class My {
int i;
public:
void play(My<Buddy>& s) {
s.i = 3;
}
};
int main() {
My<int> h;
My<Buddy> me, bud;
h.play(bud);
me.play(bud);
}
43. Why does the following code not compile?
template<class T>
double pythag(T a, T b, T c) {
return (-b + sqrt(double(b*b - 4*a*c))) / 2*a;
}
int main() {
pythag(1, 2, 3);
pythag(1.0, 2.0, 3.0);
pythag(1, 2.0, 3.0);
pythag<double>(1, 2.0, 3.0);
}
44. Write templates that take non-type parameters of the following variety: an int, a pointer to an int, a pointer to a static class member of type int, and a pointer to a static member function.
45. Write a class template that takes two type parameters. Define a partial specialization for the first parameter, and another partial specialization that specifies the second parameter. In each specialization, introduce members that are not in the primary template.
46. Define a class template named Bob that takes a single type parameter. Make Bob a friend of all instances of a template class named Friendly, and a friend of a class template named Picky only when the type parameter of Bob and Picky are identical. Give Bob member functions that demonstrate its friendship.
6: Generic algorithms
Algorithms are at the core of computing. To be able to write an algorithm once and for all to work with any type of sequence makes your programs both simpler and safer. The ability to customize algorithms at runtime has revolutionized software development.
The subset of the standard C++ library known as the Standard Template Library (STL) was originally designed around generic algorithms—code that processes sequences of any type of values in a type-safe manner. The goal was to use predefined algorithms for almost every task, instead of hand-coding loops every time you need to process a collection of data. This power comes with a bit of a learning curve, however. By the time you get to the end of this chapter, you should be able to decide for yourself whether you find the algorithms addictive or too confusing to remember. If you’re like most people, you’ll resist them at first but then tend to use them more and more.
A first look
Among other things, the generic algorithms in the standard library provide a vocabulary with which to describe solutions. That is, once you become familiar with the algorithms, you’ll have a new set of words with which to discuss what you’re doing, and these words are at a higher level than what you had before. You don’t have to say, "This loop moves through and assigns from here to there … oh, I see, it’s copying!" Instead, you just say copy( ). This is the kind of thing we’ve been doing in computer programming from the beginning—creating high-level abstractions to express what you’re doing and spending less time saying how you’re doing it. The how has been solved once and for all and is hidden in the algorithm’s code, ready to be reused on demand.
Here’s an example of how to use the copy algorithm:
//: C06:CopyInts.cpp
// Copies ints without an explicit loop
#include <algorithm>
#include <cassert>
#include <cstddef> // For size_t
using namespace std;
int main() {
int a[] = {10, 20, 30};
const size_t SIZE = sizeof a / sizeof a[0];
int b[SIZE];
copy(a, a + SIZE, b);
for (int i = 0; i < SIZE; ++i)
assert(a[i] == b[i]);
} ///:~.
The copy algorithm’s first two parameters represent the range of the input sequence—in this case the array a. Ranges are denoted by a pair of pointers. The first points to the first element of the sequence, and the second points one position past the end of the array (right after the last element). This may seem strange at first, but it is an old C idiom that comes in quite handy. For example, the difference of these two pointers yields the number of elements in the sequence. More important, in implementing copy( ), the second pointer can act as a sentinel to stop the iteration through the sequence. The third argument refers to the beginning of the output sequence, which is the array b in this example. It is assumed that the array that b represents has enough space to receive the copied elements.
The copy( ) algorithm wouldn’t be very exciting if it could only process integers. It can in fact copy any sequence. The following example copies string objects.
//: C06:CopyStrings.cpp
// Copies strings
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <string>
using namespace std;
int main() {
string a[] = {"read", "my", "lips"};
const size_t SIZE = sizeof a / sizeof a[0];
string b[SIZE];
copy(a, a + SIZE, b);
assert(equal(a, a + SIZE, b));
} ///:~.
This example introduces another algorithm, equal( ), which returns true only if each element in the first sequence is equal (using its operator==( )) to the corresponding element in the second sequence. This example traverses each sequence twice, once for the copy, and once for the comparison, without a single explicit loop!.
Generic algorithms achieve this flexibility because they are function templates, of course. If you guessed that the implementation of copy( ) looked something like the following, you’d be "almost" right.
template<typename T>