作者:jicanmeng
时间:2017年06月04日
先看一个程序:
#include <iostream> using namespace std; const int max(const int x, const int y) { return (x > y) ? x : y; } const double max(const double x, const double y) { return (x > y) ? x : y; } const char max(const char x, const char y) { return (x > y) ? x : y; } int main() { int i = max(3, 7); std::cout << i << '\n'; double d = max(6.34, 18.523); std::cout << d << '\n'; char ch = max('a', '6'); std::cout << ch << '\n'; return 0; }
以下是运行结果:
[jicanmeng@andy tmp]$ ./a.out
7
18.523
a
[jicanmeng@andy tmp]$
要实现数据类型相同的两个变量的比较,需要定义三个max()函数。这三个max()函数,除了数据类型不同,别地方都是完全相同的。如果还有其它类型的数据,那么代码就会非常庞大并且难以维护。为了解决这个问题,c++引入了函数模板。如下面代码所示:
#include <iostream> //#include <algorithm> //这个标准库中定义了max()函数模板,所以如果包含本头文件,就会和下面的max()模板的定义冲突 using namespace std; template <typename T> // this is the template parameter declaration const T& max(const T& x, const T& y) { return (x > y) ? x : y; } int main() { int i = max(3, 7); std::cout << i << '\n'; double d = max(6.34, 18.523); std::cout << d << '\n'; char ch = max('a', '6'); std::cout << ch << '\n'; return 0; }
上面代码中,T 被称为template type parameter。第5行代码中,template后面的<>中包含了所有的template type parameter。
有几点需要注意:
1. 可以定义一个template type parameter,也可以定义多个。例如:
其中,template <typename T1, typename T2> // template function here
typename
关键字也可以使用class
关键字代替,意思是相同的。
2. 由于传递给T的可能也是class类型的对象,为了防止不必要的拷贝,通常使用引用类型。learncpp.com上面的描述如下:
One final note: Because the function argument passed in for type T could be a class type, and it’s generally not a good idea to pass classes by value, it would be better to make the parameters and return types of our templated function const references. 所以就写成了下面的形式:
template <typename T> const T& max(const T& x, const T& y) { return (x > y) ? x : y; }
3. 编译器遇到max(3,7)时,会去寻找有没有max(int, int)函数。如果没有此函数,会寻找有没有对应的模板可以创建max(int, int)函数:如果有模板,那么会创建一个max(int, int)函数,这称为模板的实例(function template instance)。
4. 模板的类型参数如果传入class类型的变量, 要注意运算符的重载。
可以看两个例子:
#include <iostream> using namespace std; template <typename T> // this is the template parameter declaration const T& max(const T& x, const T& y) { return (x > y) ? x : y; } class Cents { private: int m_cents; public: Cents(int cents) : m_cents(cents) { } friend bool operator>(const Cents &c1, const Cents &c2) { return (c1.m_cents > c2.m_cents) ? true : false; } int getCents() { return m_cents; } }; int main() { Cents nickle(5); Cents dime(10); Cents bigger = max(nickle, dime); std::cout << "bigger number is " << bigger.getCents() << std::endl; return 0; }
上面的这个例子就说明了2,3,4里面的知识点。由于函数模板有>
的操作,所以必须对class CPoint类型的对象实现>
运算符重载。如果我们在模板的参数和模板的返回值中没有使用引用变量,那么就会有拷贝构造函数的调用。使用了引用变量,减少了内存拷贝,提高了程序运行的效率。
函数模板的定义中,也可以出现固定的数据类型。如下面的例子:
#include <iostream> using namespace std; template <class T> T average(T *array, int length) { T sum = 0; for (int count = 0; count < length; ++count) sum += array[count]; sum /= length; return sum; } class Cents { private: int m_cents; public: Cents(int cents) : m_cents(cents) { } friend bool operator>(const Cents &c1, const Cents &c2) { return (c1.m_cents > c2.m_cents); } friend ostream& operator<< (ostream &out, const Cents &cCents) { out << cCents.m_cents << " cents "; return out; } Cents & operator+= (Cents &c1); Cents & operator/= (int length); }; Cents & Cents::operator+= (Cents &c1) { m_cents += c1.m_cents; return *this; } Cents & Cents::operator/= (int length) { m_cents /= length; return *this; } int main() { int array1[] = { 5, 3, 2, 1, 4 }; std::cout << average(array1, 5) << '\n'; double array2[] = { 3.12, 3.45, 9.23, 6.34 }; std::cout << average(array2, 4) << '\n'; Cents array3[] = { Cents(5), Cents(10), Cents(15), Cents(14) }; std::cout << average(array3, 4) << '\n'; return 0; }