《C++ Templates中文版》——2.4 重载函数模板

简介:

本节书摘来自异步社区出版社《C++ Templates中文版》一书中的第2章,第2.4节,作者: 【美】David Vandevoorde , 【德】Nicolai M. Josuttis,更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.4 重载函数模板

和普通函数一样,函数模板也可以被重载。就是说,相同的函数名称可以具有不同的函数定义;于是,当使用函数名称进行函数调用的时候,C++编译器必须决定究竟要调用哪个候选函数。即使在不考虑模板的情况下,做出该决定的规则也已经是相当复杂,但在这一节里,我们将讨论有关模板的重载问题。如果你对不含模板的重载的基本规则还不是很熟悉,那么请先阅读附录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节讨论这个细节;但就目前而言,你应该牢记一条首要规则:函数的所有重载版本的声明都应该位于该函数被调用的位置之前。

相关文章
|
20天前
|
编译器 C语言 C++
c++的学习之路:19、模板
c++的学习之路:19、模板
32 0
|
1月前
|
存储 C++ 容器
C++STL(标准模板库)处理学习应用案例
【4月更文挑战第8天】使用C++ STL,通过`std:vector`存储整数数组 `{5, 3, 1, 4, 2}`,然后利用`std::sort`进行排序,输出排序后序列:`std:vector<int> numbers; numbers = {5, 3, 1, 4, 2}; std:sort(numbers.begin(), numbers.end()); for (int number : numbers) { std::cout << number << " "; }`
21 2
|
1月前
|
编译器 C++
【C++初阶】13. 模板进阶
【C++初阶】13. 模板进阶
28 2
|
1月前
|
C++
C++当类模板遇到static
C++当类模板遇到static
|
4天前
|
存储 算法 编译器
C++的模板与泛型编程探秘
C++的模板与泛型编程探秘
9 0
|
4天前
|
编译器 C++
【C++从练气到飞升】08---模板
【C++从练气到飞升】08---模板
|
5天前
|
算法 编译器 C++
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]
22 1
|
6天前
|
编译器 C语言 C++
【C++】模板进阶
【C++】模板进阶
13 1
|
6天前
|
存储 编译器 C++
【C++】内存管理和模板基础(new、delete、类及函数模板)
【C++】内存管理和模板基础(new、delete、类及函数模板)
21 1
|
12天前
|
C++
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】