作者: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;
}