《C++ Templates中文版》——2.4 重载函数模板-阿里云开发者社区

开发者社区> 异步社区> 正文

《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节讨论这个细节;但就目前而言,你应该牢记一条首要规则:函数的所有重载版本的声明都应该位于该函数被调用的位置之前。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
在smarty模板中使用PHP函数的方法
sample1 复制代码 代码如下: 那如果使用像iconv这样的有三个参数的函数该怎么写呢?如果写成: sample 2 复制代码 代码如下: 一执行就会发现显示error信息。 因此研究一下就会发现,起始在smarty模板页的套用函数用法中,以smaple 1来说,trim的前面$Row->colname其实就是trim的第一个参数,中间用|这个符号串接; 那假设要使用像iconv有三个参数的函数的话,就要写成: sample 3 复制代码 代码如下: 也就是 函数第一个参数|函数:第二个参数:第三个参数。
881 0
创建卡券模板接口(alipay.pass.template.add)JAVA版本小样
官方接口文档:https://docs.open.alipay.com/199/105250/  基本操作流程  1.创建应用等流程不概述了,调用该接口主要第一步是模板设计:推荐使用卡券平(https://alipassprod.
459 0
会员卡开卡表单模板配置(alipay.marketing.card.formtemplate.set)JAVA版本demo
官方接口文档:[url]https://docs.open.alipay.com/251/105668/[/url],我这里写的是开卡组件前两步的操作流程,仅供参考 1.调用接口前的准备工作(创建应用等)参考该文档:[url]https://docs.
743 0
Java Template Pattern(模板模式)
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
581 0
第8周-任务1-方案2-复数类中运算符重载(非成员函数实现)
接:第8周-任务1-方案1-复数类中运算符重载(成员函数实现) 本文用方案二求解:用类的友元函数,而不是成员函数,完成上面提及的运算符的重载; 【讲解视频】 【参考解答】 #include &lt;iostream&gt; using namespace std; class Complex { public: Complex(){real=0;imag=0;}
939 0
+关注
异步社区
异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
12049
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载