设为首页 加入收藏

TOP

2.4 重载函数模板
2013-10-07 00:06:03 来源: 作者: 【 】 浏览:66
Tags:2.4 重载 函数 模板

2.4  重载函数模板

和普通函数一样,函数模板也可以被重载。就是说,相同的函数名称可以具有不同的函数定义;于是,当使用函数名称进行函数调用的时候,C++(www.cppentry.com)编译器必须决定究竟要调用哪个候选函数。即使在不考虑模板的情况下,做出该决定的规则也已经是相当复杂,但在这一节里,我们将讨论有关模板的重载问题。如果你对不含模板的重载的基本规则还不是很熟悉,那么请先阅读附录B,在那里我们对重载解析规则进行了很详细的叙述。

下面的简短程序叙述了如何重载一个函数模板:

//basics/max2.cpp
//求两个int值的最大值
inline int const& max (int const& a, int const& b)
{
return  a < b b : a;
}

// 求两个任意类型值中的最大者
template <typename T>
inline T const& max (T const& a, T const& b)
{
return  a < b b : a;
}

// 求3个任意类型值中的最大者
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return ::max (::max(a,b), c);
}

int main()
{
::max(7, 42, 68);      // 调用具有3个参数的模板
::max(7.0, 42.0);     // 调用max<double> (通过实参演绎)
::max('a', 'b');      // 调用max<char> (通过实参演绎)
::max(7, 42);          // 调用int重载的非模板函数
::max<>(7, 42);       // 调用 max<int> (通过实参演绎)
::max<double>(7, 42); //调用max<double> (没有实参演绎)
::max('a', 42.7);    //调用int重载的非模板函数
}

如例子所示,一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。对于非模板函数和同名的函数模板,如果其他条件都是相同的话,那么在调用的时候,重载解析过程通常会调用非模板函数,而不会从该模板产生出一个实例。第4个调用就符合这个规则:

max(7,42)    //使用两个int值,很好地匹配非模板函数

然而,如果模板可以产生一个具有更好匹配的函数,那么将选择模板。这可以通过max()的第2次和第3次调用来说明:

max(7.0,42.0);   //调用 max<double>(通过实参演绎)

max('a', 'b');   //调用 max<char>(通过实参演绎)

还可以显式地指定一个空的模板实参列表,这个语法好像是告诉编译器:只有模板才能来匹配这个调用,而且所有的模板参数都应该根据调用实参演绎出来:

max<>(7,42)    //call max<int>(通过实参演绎)

因为模板是不允许自动类型转化的;但普通函数可以进行自动类型转换,所以最后一个调用将使用非模板函数(‘a’和42.7都被转化为int):

max('a',42.7)   //对于不同类型的参数,只允许使用非模板函数

下面这个更有用的例子将会为指针和普通的C字符串重载这个求最大值的模板:

//basics/max3.cpp
#include <iostream>
#include <cstring>
#include <string>

// 求两个任意类型值的最大者
template <typename T>
inline T const& max (T const& a, T const& b)
{
return  a < b    b : a;
}

// 求两个指针所指向值的最大者
template <typename T>
inline T* const& max (T* const& a, T* const& b)
{
return  *a < *b    b : a;
}

// 求两个C字符串的最大者
inline char const* const& max (char const* const& a,
char const* const& b)
{
return  std::strcmp(a,b) < 0    b : a;
}

int main ()
{
int a=7;
int b=42;
::max(a,b);      // max() 求两个int值的最大值

    std::string s="hey";
std::string t="you";
::max(s,t);     // max() 求两个std:string类型的最大值

    int* p1 = &b;
int* p2 = &a;
::max(p1,p2);    // max() 求两个指针所指向值的最大者

    char const* s1 = "David";
char const* s2 = "Nico";
::max(s1,s2);    // max() 求两个c字符串的最大值
}

注意,在所有重载的实现里面,我们都是通过引用来传递每个实参的。一般而言,在重载函数模板的时候,最好只是改变那些需要改变的内容;就是说,你应该把你的改变限制在下面两种情况:改变参数的数目或者显式地指定模板参数。否则就可能会出现非预期的结果。例如,对于原来使用传引用的max()模板,你用C-string类型进行重载;但对于现在(即重载版本的)基于C-strings的max()函数,你是通过传值来传递参数;那么你就不能使用3个参数的max()版本,来对3个C-string求最大值:

//basics/max3a.cpp
#include <iostream>
#include <cstring>
#include <string>

// 两个任意类型值的最大者 (通过传引用进行调用)
template <typename T>
inline T const& max (T const& a, T const& b)
{
return  a < b    b : a;
}

// 两个C字符串的最大者 (通过传值进行调用)
inline char const* max (char const* a, char const* b)
{
return  std::strcmp(a,b) < 0    b : a;
}

// 求3个任意类型值的最大者 (通过传引用进行调用)
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return max (max(a,b), c);  //注意:如果max(a,b)使用传值调用
//那么将会发生错误
}

int main ()
{
::max(7, 42, 68);     // OK

    const char* s1 = "frederic";
const char* s2 = "anica";
const char* s3 = "lucas";
::max(s1, s2, s3);    // 错误。

}

问题在于:如果你对3个C-strings调用max(),那么语句:

return max (max(a,b),c);

将会产生一个错误。这是因为对于C-strings而言,这里的max(a,b)产生了一个新的临时局部值,该值有可能会被外面的max函数以传引用的方式返回,而这将导致传回无效的引用。

对于复杂的重载解析规则所产生的结果,这只是具有非预期行为的代码例子中的一例而已。例如,当调用重载函数的时候,调用结果就有可能与该重载函数在此时可见与否这个事实有关,但也可能没有关系。事实上,定义一个具有3个参数的max()版本,而且直到该定义处还没有看到一个具有两个int参数的重载max()版本的声明;那么这个具有3个int实参的max()调用将会使用具有2个参数的模板,而不会使用基于int的重载版本max():

//basics/max4.cpp
// 求两个任意类型值的最大者
template <typename T>
inline T const& max (T const& a, T const& b)
{
return  a < b b : a;
}

// 求3个任意类型值的最大者
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return max (max(a,b), c);      //使用了模板的版本,即使有下面声明的int                                   
//版本,但该声明来得太迟了
}
// 求两个int值的最大者
inline int const& max (int const& a, int const& b)
{
return  a < b b : a;
}

我们将在9.2节讨论这个细节;但就目前而言,你应该牢记一条首要规则:函数的所有重载版本的声明都应该位于该函数被调用的位置之前。


回书目   上一节   下一节

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇2.2 实参的演绎 (deduction) 下一篇4.1 非类型的类模板参数

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: