1、定义函数模板
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 template <typename T> 6 int compare(const T &v1, const T &v2) 7 { 8 if(v1 < v2) 9 return -1; 10 if(v2 < v1) 11 return 1; 12 return 0; 13 } 14 15 int main() 16 { 17 // T is int---compiler instantiates int compare(const int&, const int&) 18 cout << compare(1, 0) << endl; 19 // T is string---compiler instantiates int compare(const string&, const string&) 20 string s1 = "hi", s2 = "world"; 21 cout << compare(s1, s2) << endl; 22 system("pause"); 23 return 0; 24 }
inline函数模板---注意:模板函数可以用与非模板函数一样的方式声明为 inline。说明符放在模板形参表之后、返回类型之前,不能放在关键字 template 之前
1 // ok:inline specifier follows templacement of inline specifier 2 template <typename T> inline T min(const T&, const T&); 3 // error: incorrect placement f inline specifier 4 inline template <typename T> T min(const T&, const T&);
定义多个模板形参:
1 /** 2 * 编写一个函数模板,接受一个ostream引用和一个值,将该值写入流。用至少四种不同类型调用函数。 3 *通过写至cout、写至文件和写至stringstream来测试你的程序。 4 */ 5 6 #include <iostream> 7 #include <string> 8 #include <fstream> 9 #include <sstream> 10 using namespace std; 11 12 template <typename T1, typename T2> 13 T1& print(T1& s, T2 val) 14 { 15 s << val; 16 return s; 17 } 18 19 int main() 20 { 21 double dval = 0.88; 22 float fval = -12.3; 23 string oristr = "this is a test", desstr; 24 ostringstream oss(desstr); 25 ofstream outFile("result.dat"); 26 27 // 写至 cout 28 print(cout, -3) << endl; 29 print(cout, dval) << endl; 30 print(cout, fval) << endl; 31 print(cout, oristr) << endl; 32 33 // 写至文件 34 print(outFile, -3) << endl; 35 print(outFile, dval) << endl; 36 print(outFile, fval) << endl; 37 print(outFile, oristr) << endl; 38 outFile.close(); 39 40 // 写至 stringstream 41 print(oss, -3) << endl; 42 print(oss, dval) << endl; 43 print(oss, fval) << endl; 44 print(oss, oristr) << endl; 45 46 // 将 stringstream 中的字符串输出到cout以进行验证 47 cout << oss.str() << endl; //oss.str()返回的是 string 类型的字符串 48 49 system("pause"); 50 return 0; 51 }
注意:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 template <typename T> 6 int compare(const T &v1, const T &v2) 7 { 8 if(v1 < v2) 9 return -1; 10 if(v2 < v1) 11 return 1; 12 return 0; 13 } 14 15 int main() 16 { 17 // Error 18 cout << compare("hi", "world") << endl; 19 /* 这样调用将会出编译错误,因为根据第一个实参"hi" 可将模板形参T推断为char[3], 20 而第二个实参"world"可将模板形参T推断为char[6],T被推断为两个不同的类型,编译器无法实例化。*/ 21 22 system("pause"); 23 return 0; 24 }
2、定义类模板
1 // 编写类似于标准库中 find 算法的模板函数。使用该函数在 vector<int> 和 vector<string>中查找指定值 2 #include <iostream> 3 #include <string> 4 #include <vector> 5 using namespace std; 6 7 //template <typename InIt, typename T> 8 template <class InIt, class T> // 使用 class 或 typename 关键字都可以 9 InIt findElem(InIt first, InIt last, const T& val) 10 { 11 while(first != last) 12 { 13 if(*first == val) 14 return first; 15 ++first; 16 } 17 return last; 18 } 19 20 int main() 21 { 22 int ia[] = {1, 2, 3, 4, 5, 6, 7}; 23 string sa[] = {"this", "is", "Mary", "test", "example"}; 24 vector<int> ivec(ia, ia+7); 25 vector<string> svec(sa, sa+5); 26 27 vector<int>::iterator itor; 28 if((itor = findElem(ivec.begin(), ivec.end(), 6)) != ivec.end()) 29 cout << "found this element: " << *itor << endl; 30 else 31 cout << "no such element" << endl; 32 33 vector<string>::iterator itor1; 34 if((itor1 = findElem(svec.begin(), svec.end(), "Mary")) != svec.end()) 35 cout << "found this element: " << *itor1 << endl; 36 else 37 cout << "no such element" << endl; 38 39 system("pause"); 40 return 0; 41 }
如果要在函数模板内部使用在类中定义的类型成员,必须在该成员名前加上关键字typename,告诉编译器将该成员当做类型。
1 /** 2 * 编写一个函数,接受一个容器引用并打印该容器的元素, 3 * 4 */ 5 // 代码一:使用容器的 size_type 和 size成员控制打印元素的循环。 6 #include <iostream> 7 #include <string> 8 #include <vector> 9 using namespace std; 10 11 template <typename Parm> 12 void print(const Parm& c) 13 { 14 typename Parm::size_type index = 0; 15 while(index != c.size()) 16 { 17 cout << c[index] << ' '; 18 ++index; 19 } 20 } 21 22 int main() 23 { 24 int ia[] = {1, 2, 1, 4, 1, 6, 1}; 25 string sa[] ={"this", "is", "Mary", "test", "example"}; 26 vector<int> ivec(ia, ia+7); 27 vector<string> svec(sa, sa+5); 28 print(ivec); 29 cout << endl; 30 print(svec); 31 32 system("pause"); 33 return 0; 34 } 35 36 // 代码二:使用 begin 和 end 返回迭代器来控制循环 37 #include <iostream> 38 #include <string> 39 #include <vector> 40 using namespace std; 41 42 template <typename Parm> 43 void print(const Parm& c) 44 { 45 typename Parm::const_iterator itor = c.begin(); 46 while(itor != c.end()) 47 { 48 cout << *itor++ << ' '; 49 } 50 } 51 52 int main() 53 { 54 int ia[] = {1, 2, 1, 4, 1, 6, 1}; 55 string sa[] ={"this", "is", "Mary", "test", "example"}; 56 vector<int> ivec(ia, ia+7); 57 vector<string> svec(sa, sa+5); 58 print(ivec); 59 cout << endl; 60 print(svec); 61 62 system("pause"); 63 return 0; 64 }
模板形参也可以是非类型的:模板非类型形参是模板定义内部的常量值,在需要常量表达式的时候,可使用非类型形参
例如:编写可以确定数组长度的函数模板
1 template <typename T, std::size_t N> 2 std::size_t size(T (&arr)[N]) 3 { 4 return N; 5 }
对于模板类型形参,传给它的实参必须完全匹配,如果想要用兼容类型调用,可以使用显示模板实参或者强制类型转换(类型转换的限制只适用于类型为模板形参的那些实参,非模板类型的形参可以隐式转换)
1 #include <iostream> 2 using namespace std; 3 4 template <typename T> 5 int compare(const T& v1, const T& v2) 6 { 7 if(v1 < v2) 8 return -1; 9 if(v2 < v1) // 注意这里不要写成 v1 > v2, 在泛型编程中注意规范 10 return 1; 11 return 0; 12 } 13 14 int main() 15 { 16 short sval = 123; 17 int ival = 1024; 18 //cout << compare(sval, ival) << endl; // ERROR:cannot instantiate compare(short, int), 形参必须完全匹配 19 // 转换类型 20 cout << compare(static_cast<int>(sval), ival) << endl; 21 cout << compare(sval, static_cast<short>(ival)) << endl; 22 // 显示指定形参类型 23 cout << compare<short>(sval, ival) << endl; 24 cout << compare<int>(sval, ival) << endl; 25 26 system("pause"); 27 return 0; 28 }
一般而言,不会转换实参以匹配已有的实例化,相反,会产生新的实例。除产生新的实例外,编译器只会执行两种转化:
1、const 转换:接收const 引用或 const 指针的函数可以分别用非 const 对象的引用或指针来调用,无须产生新的实例化。如果函数接受非引用类型,形参类型和实参都忽略const,即,无论传递 const 或 非 const 对象给接受非引用类型的函数,都使用相同的实例化。
2、数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换,数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。举例说明如下:
1 template <typename T> T fobj(T, T); // arguments are copied 2 template <typename T> T fref(const T&, const T&); // reference argements 3 4 /* 5 传递 string 对象和 const string 对象作为实参,即使这些类型不完全匹配,两个调用也都是合法的。 6 在 fobj 的调用中,实参被复制,因此原来的对象是否为 const 无关紧要,在 fref 的调用中,形参类型是 7 const 引用,对引用形参而言,转换为 const 是可以接受的转换,所以这个调用也正确 8 */ 9 string s1("a value"); 10 const string s2("another value"); 11 fobj(s1, s2); // ok: calls f(string, string), const is ignored 12 fref(s1, s2); // ok:non const object s1 converted to const reference 13 14 15 /* 16 传递不同长度的数组实参--fobj 的调用中,数组不同无关紧要,两个数组都转换为指针, 17 fobj 的模板形参类型是 int*,但是,fref的调用是非法的,当形参为引用时数组不能转换为指针, 18 a 和 b 的类型不匹配(引用类型会检查数组长度),所以调用会出错 19 */ 20 int a[10], b[42]; 21 fobj(a, b); // ok: calls f(int*, int*) 22 fref(a, b); // error:array types don't match; arguments aren't converted to pointers