In main( ) you can see that type deduction works here too. The first call to h( ) explicitly gives the template argument for f, but since h( ) says that it will only take the address of a function that takes an int*, that part can be deduced by the compiler. With g( ) the situation is even more interesting because two templates are involved. The compiler cannot deduce the type with nothing to go on, but if either f or g is given int, the rest can be deduced.
An obscure issue arises when trying to pass the functions tolower or toupper, declared in <cctype>, as parameters. It is possible to use these in conjunction with the transform algorithm (which is covered in detail in the next chapter), for example, to convert a string to lower or upper case. Care must be taken, however, because there are multiple declarations for these functions. A naïve approach would is something like this:.
// The variable s is a std::string
transform(s.begin(), s.end(), s.begin(), tolower);
The transform algorithm applies its fourth parameter (tolower in this case) to each character in the string s and places the result in s itself, thus overwriting each character in s with its lower-case equivalent. As it is written, this statement may or may not work! It fails in the following context:.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
int main() {
string s("LOWER");
transform(s.begin(),s.end(),s.begin(),tolower);
cout << s << endl;
}
Even if your compiler let’s you get away with this, it is illegal. The reason is that the <iostream> header also makes available a two-argument version of tolower and toupper:
template <class charT> charT toupper(charT c,
const locale& loc);
template <class charT> charT tolower(charT c,
const locale& loc);
These function templates take a second argument of type locale. The compiler has no way of knowing whether it should use the one-argument version of tolower defined in <cctype> or the one mentioned above. You can solve this problem (almost!) with a cast in the call to transform, as follows:.
transform(s.begin(),s.end(),s.begin()
static_cast<int (*)(int)>(tolower));
(Recall that tolower and toupper traffic in int instead of char.) The cast above makes clear that the single-argument version of tolower is desired. Once again, this works with some compilers, but it is not required to. The reason, albeit obscure, is that a library implementation is allowed to give "C linkage" (meaning that the function name does not contain all the auxiliary information[54] that normal C++ functions do) to functions inherited from the C language. If this is the case, the cast fails, because transform is a C++ function template and expects its fourth argument to have C++ linkage—and a cast is not allowed to change the linkage. What a predicament!.
The solution is to place calls to tolower in an unambiguous context. For example, you could write a function, let’s call it strTolower( ), and place it in its own file without including <iostream>, like this:.
//: C05:StrTolower.cpp {O}
#include <algorithm>
#include <cctype>
#include <string>
using namespace std;
string strTolower(string s) {
transform(s.begin(), s.end(), s.begin(), tolower);
return s;
} ///:~
The header <iostream> is not involved here, and the compilers we use do not introduce the two-argument version of tolower in this context,[55] so there’s no problem. You can then use this function normally:.
//: C05:Tolower.cpp
//{L} StrTolower
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
string strTolower(string);
int main() {
string s("LOWER");
cout << strTolower(s) << endl;
} ///:~
Another solution is to write a wrapper function template that calls the correct version of tolower explicitly:
//: C05:ToLower2.cpp
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
template<class charT>
charT strTolower(charT c) {
return tolower(c); // one-arg version called
}
int main() {
string s("LOWER");
transform(s.begin(),s.end(),s.begin(),&strTolower<char>);
cout << s << endl;
} ///:~
This version has the advantage that it can process both wide and narrow strings since the underlying character type is a template parameter. The C++ standards committee is working on modifying the language so that the first example (without the cast) will work, and some day these workarounds can be ignored.[56]
Applying a function to an STL sequence
Suppose you want to take an STL sequence container (which you’ll learn more about in subsequent chapters; for now we can just use the familiar vector) and apply a function to all the objects it contains. Because a vector can contain any type of object, you need a function that works with any type of vector:.
//: C05:ApplySequence.h
// Apply a function to an STL sequence container
// 0 arguments, any type of return value:
template<class Seq, class T, class R>
void apply(Seq& sq, R (T::*f)()) {
typename Seq::iterator it = sq.begin();
while(it != sq.end()) {
((*it)->*f)();
it++;
}
}
// 1 argument, any type of return value:
template<class Seq, class T, class R, class A>
void apply(Seq& sq, R(T::*f)(A), A a) {
typename Seq::iterator it = sq.begin();
while(it != sq.end()) {
((*it)->*f)(a);
it++;
}
}
// 2 arguments, any type of return value:
template<class Seq, class T, class R,
class A1, class A2>