Source of the problem -Accelerated C++, problem 8-5
I've written a small program that examines lines of string input, and tallies the number of times a word appears on a given line. The following code accomplishes this:
#include <map>
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <cctype>
#include <iterator>
using std::vector; using std::string;
using std::cin; using std::cout;
using std::endl; using std::getline;
using std::istream; using std::string;
using std::list; using std::map;
using std::isspace; using std::ostream_iterator;
using std::allocator;
inline void keep_window_open()
{
cin.clear();
cout << "Please enter EOF to exit\n";
char ch;
cin >> ch;
return;
}
template <class Out>
void split(const string& s, Out os)
{
vector<string> ret;
typedef string::size_type string_size;
string_size i = 0;
// invariant: we have processed characters `['original value of `i', `i)'
while (i != s.size()) {
// ignore leading blanks
// invariant: characters in range `['original `i', current `i)' are all spaces
while (i != s.size() && isspace(s[i]))
++i;
// find end of next word
string_size j = i;
// invariant: none of the characters in range `['original `j', current `j)' is a space
while (j != s.size() && !isspace(s[j]))
++j;
// if we found some nonwhitespace characters
if (i != j) {
// copy from `s' starting at `i' and taking `j' `\-' `i' chars
*os++ = (s.substr(i, j - i));
i = j;
}
}
}
// find all the lines that refer to each word in the input
map<string, vector<int> > xref(istream& in) // works
// now try to pass the template function as an argument to function - what do i put for templated type?
//map<string, vector<int> > xref(istream& in, void find_words(vector<string, typedef Out) = split) #LINE 1#
{
string line;
int line_number = 0;
map<string, vector<int> > ret;
// read the next line
while (getline(in, line)) {
++line_number;
// break the input line into words
vector<string> words; // works // #LINE 2#
split(line, back_inserter(words)); // #LINE 3#
//find_words(line, back_inserter(words)); // #LINE 4# attempting to use find_words as an argument to function
// remember that each word occurs on the current line
for (vector<string>::const_iterator it = words.begin();
it != words.end(); ++it)
ret[*it].push_back(line_number);
}
return ret;
}
int main()
{
cout << endl << "Enter lines of text, followed by EOF (^Z):" << endl;
// call `xref' using `split' by default
map<string, vector<int> > ret = xref(cin);
// write the results
for (map<string, vector<int> >::const_iterator it = ret.begin();
it != ret.end(); ++it) {
// write the word
cout << it->first << " occurs on line(s): ";
// followed by one or more line numbers
vector<int>::const_iterator line_it = it->second.begin();
cout << *line_it; // write the first line number
++line_it;
// write the rest of the line numbers, if any
while (line_it != it->second.end()) {
cout << ", " << *line_it;
++line_it;
}
// write a new line to separate each word from the next
cout << endl;
}
keep_window_open();
return 0;
}
As you can see, the split function is a template function to handle various types of output iterators as desired.
My problem comes when I try to generalize the xref function by passing in the templated split function as an argument. I can't seem to get the type correct.
So my question is, can you pass a template function to another function as an argument, and if so, do you have to declare all types before passing it? Or can the compiler infer the types from the way the templated function is used in the body?
To demonstrate the errors I get, comment out the existing xref function header, and uncomment the alternate header I'm trying to get working (just below the following commment line.) Also comment the lines tagged LINE 2 and LINE 3 and uncomment LINE 4, which is attempting to use the argument find_words (which defaults to split.)
Thanks for any feedback!