在<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 ;
}
如果聲明的是非引用參數,就可以使用長度不同的字元串作為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 ;
}
産生在這種調用的原因是:對于非引用類型的參數,在實參演繹的過程中會出現數組到指針(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 ;
}
在上述的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>。