在<C++基础——一些细节、常犯错误的汇总>一文的细节3中,我们看到,
const char* s = "hello";
s
的类型是const char*
,"hello"
的类型是const char[6]
是数组类型,也就是与"hell"
(const char[5])具有不同的数据类型。
这一点在函数模板(以字符串为参数)的设计中,显得尤为重要。
template<typename T>
inline const T& max(const T& x, const T& y)
{
return x > y ? x : y;
}
int main(int, char**)
{
::max("hello", "world"); // 正确,具有相同的实参类型,const char[6],
// 这样在类型推导时都能匹配到同一个类型
::max("hello", "hell"); // 错误, 不同类型的实参
std::string s("world");
::max("hello", s); // 错误, const char[6] 与 string是不同的类型
return 0;
}
如果声明的是非引用参数,就可以使用长度不同的字符串作为max()的参数:
template<typename T>
inline T max(T x, T y)
{
return x > y ? x : y;
}
int main(int, char**)
{
::max("hello", "world"); // 正确,具有相同的实参类型,const char[6],
// 这样在类型推导时都能匹配到同一个类型
::max("hello", "hell"); // 正确, decay(退化)为相同的类型
std::string s("world");
::max("hello", s); // 错误,不同的类型
return 0;
}
产生在这种调用的原因是:对于非引用类型的参数,在实参演绎的过程中会出现数组到指针(pointer-to-array)的类型转换(这种转型通常也被称为decay),果真如此奇妙吗,我们不妨使用typeid
关键字加以验证:
template<typename T>
void ref(const T& x)
{
cout << "x in ref(const T&): " << typeid(x).name() << endl;
}
template<typename T>
void noref(T x)
{
cout << "x in noref(T): " << typeid(x).name() << endl;
}
int main(int, char**)
{
::ref("hello"); // const char [6]
::noref("hello"); // const char*
return 0;
}
在上述的main函数中,分别将一个字符串(”hello”,类型为const char [6])传递给具有引用参数的函数模板和具有非引用参数的函数模板。通过打印的结果我们可以看到,参数为引用的函数模板和参数不是引用的函数模板在对待字符数组和字符串指针之间存在着一些隐蔽的不同。对这一问题并没有太好的方法,根据不同的情况,可以:
使用非引用参数,取代引用参数,
杀敌一千,自损八百,导致无用的拷贝
重载版本1,分别编写参数为引用类型地函数模板,和参数为非引用类型的函数模板
这可能会导致二义性的出现;
重载版本2,重载数组类型
template<typename T, int N, int M> // 存在两个非类型模板参数
inline const T* max(const T (*x)[N], const T (*y)[M])
{
return x > y ? x : y;
}
事实上我们更倾向于使用,std::string
c++风格的字符串类,而不是c风格字符串类,这又牵涉到C++和C语言风格的问题了。C++是一种语言联邦,支持多编程范式。我们在之前的文章中有谈到,两种语言风格下的类型转换问题,<C++基础——C++风格的类型转换(static_cast、const_cast、dynamic_cast、reinterpret_cast>。