C++ template使用字符串作为函数模板的实参(5.6节)

有时,把字符串传递给函数模板的引用参数会导致出人意料的运行结果。考虑下面的程序:
 

#include <iostream>
#include <string>

template <typename T>
inline T const& max(T const &a, T const &b)
{
    return a < b ? b : a;
}

int main()
{
    std::string s;
    std::string s1 = "apple";
    std::string s2 = "peach";
    std::cout << "max=======" << ::max(20, 10) << std::endl;    //OK:相同类型的实参
    std::cout << "max=======" << ::max(s1, s2) << std::endl;    //OK:相同类型的实参

    //::max("apple", "peach");      //不能用这种参数,会报无法从 const char *转换为 const T的错误
    //::max("apple", "tomato");   //ERROR:不同类型的实参
    //::max("apple", s);          //ERROR:不同类型的实参
    return 0;
}

问题在于:由于长度的区别,这些字符串属于不同的数组类型。也就是说,'apple'和'peach'具有相同的类型char const[6];
然而‘tomato’的类型则是:char const[7]。因此只有第一个调用是合法的,因为该max()模板期望的是类型完全相同的参数。
然而,如果声明的是非引用参数,你就可以使用长度不同的字符串来作为max()的参数:
 

#include <iostream>
#include <string>

template <typename T>
inline T const max(T const a, T const b)
{
    return a < b ? b : a;
}

int main()
{
    std::string s;
    std::cout << "max=======" << ::max("apple", "peach") << std::endl;    //OK:相同类型的实参
    std::cout << "max=======" << ::max("apple", "tomato") << std::endl;    //OK:退化(decay)为相同的类型
    //::max("apple", s);          //ERROR:不同类型的实参
    return 0;
}

产生这种调用结果的原因是:对于非引用类型的参数,在实参演绎的过程中,会出现数组到指针(array-to-pointer)的类型转换
(这种转型通常也被称为decay)。我们可以通过下面的程序来说明这一点:
 

#include <typeinfo>
#include <iostream>

template <typename T>
void ref(T const &x)
{
    std::cout << "x in ref(T cosnt &): " << typeid(x).name() << std::endl;
}

template <typename T>
void nonref(T x)
{
    std::cout << "x in nonref(T): " << typeid(x).name() << std::endl;
}

int main()
{
    ref("hello");
    nonref("hello");
    return 0;
}

在main函数中,分别传递一个字符串给具有引用参数的函数模板和具有非引用参数的函数模板。两个函数模板都使用了typeid运行符来输出
被实例化参数的类型。typeid运行符会返回std::type_info类型的左值(lvalue),其中std::type_info封装了传递给typeid运行会的表达式的类型表示;
而且,调用std::type_info的成员函数name()是为了返回类型的可读文本表示。虽然C++标准并没有要求name()必须返回一个有意义的值,但对于大多数
优秀的C++编译器实现而言,name()会返回一个字符串,清楚地表示传递给typeid的参数(或表达式)的类型(在某些实现中,这个字符串可能不是可读的文本,
但存在一个文本转换器,可以把它转换成可读的文本)。例如,上面的程序运行可能会有如下输出:


如果你遇到一个关于字符数组和字符串指针之间不匹配的问题,你会意外地发现和这个问题会有一定的相似之处。然而遗憾的是,对于这个问题并没有通用的解决方法。
根据不同的情况,你可以:
使用非引用参数,取代引用参数(然而,这可能会导致无用的拷贝)。
进行重载,编写接收引用参数和非引用参数的两个重载函数(然而,这可能会导致二义性,具体见附录B2.2)。
对具体类型进行重载(譬如对std::string进行重载)。
重载数组类型,譬如:
template <typename T, int N, int M>
T const* max(T const (&a)[N], T const (&b)[M])
{
    return a < b ? b : a;
}
强制要求应用程序 程序员使用显示类型转换。
对于我们讨论的例子,最好的方法是为字符串重载max()(见2.4节)。无论如何,为字符串提供重载都是有必要的;
因为如果不提供重载,当我们调用max()来比较两个字符串时,操作a < b执行的是指针比较,就是说a < b比较的是两个字符串的地址,
而不是它们的字典顺序。事实上,这也是我们趋向于使用诸如std::string的字符串类,而不使用C风格字符串类型的另一个原因。
关于更多的细节,请参见11.1节。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值