隱式類型轉換是C++的一個既好又壞的特性。它給人以方便,但可能形成一些十分隱晦的錯誤。編程
類型轉換提供了一個類型向另外一個類型的構造。函數
class X { public: operator int() const noexcept { return 42; } }; void Func(int) {} int wmain() { X x0; X x1; Func(x0); Func(x1); int n = x0 + x1; std::cout << n << std::endl; // 84 return 0; }
上面的代碼中,X能夠隱式地轉換爲int,因而函數Func能夠接受X類型的參數,x0與x1也能夠用+來作運算。編碼
在實際的編程工做中,一個更常見的例子是,咱們本身定義的字符串類(記爲String)重載了operator const wchar_t*():spa
class String { public: operator const wchar_t*() const noexcept { // 函數體 } };
從而,若是一個函數須要const wchar_t*類型的參數,就能夠直接傳入一個String實例。code
可是,重載類型轉換也不是萬無一失的。好比,重載operator bool()。對象
重載operator bool()所帶來的問題比較多,以致於不少企業的編碼規範中,都不提倡甚至禁止重載operator bool()。ci
因爲bool屬於算數類型,因此重載了operator bool()的類的實例能夠被用在任何須要算術類型的上下文中。字符串
class Y { private: int m_; public: explicit Y(int m) :m_{ m } {} operator bool() const noexcept { return (m_ != 0); } }; int wmain() { Y y0{ 12 }; Y y1{ 25 }; auto n = y0 + y1; // !!! std::cout << n << std::endl; return 0; }
毫無心義的y0 + y1居然(無警告地)編譯經過,並且還經過+產生了一個int,這實在不合理。可能程序做者想要的是Y(38),更可能的是後來維護代碼的人根本沒法知道原做者想幹什麼。隨着代碼的規模變大,這些細微的隱患會越埋越深,或許,未來花費兩天時間找到的BUG就是由它引發的。it
爲了防止這樣的異常狀況,C++11引入了顯式的類型轉換運算符。編譯
class X { public: explicit operator int() const noexcept { return 42; } }; void Func(int) {} int wmain() { X x0; Func(x0); // 錯誤,不存在從 X 到 int 的(隱式)轉換 int y = x0; // 錯誤,不存在從 X 到 int 的(隱式)轉換 Func((int)x0); // 正確1 Func(int(x0)); // 正確2 Func(static_cast<int>(x0)); // 正確3 return 0; }
用explicit修飾的類型轉換運算符,則相應的類型轉換必須顯式地進行。C式(正確1),函數式(正確2),static_cast(正確3)都行。
可是,顯式的類型轉換有一個例外。若是表達式被用做條件,那麼顯式的operator bool()也能夠隱式地進行(僅限轉換到bool)。「被用做條件」即:
if、while及do語句的條件部分;
for語句頭的條件表達式;
邏輯非運算符(!)、邏輯或運算符(||)、邏輯與運算符(&&)的運算對象;
條件運算符(x ? y : z)的條件表達式。
因爲轉換到bool通常被用做條件,因此operator bool()通常用explicit來修飾。
class K { public: explicit operator bool() const noexcept { return false; } }; int wmain() { K k0; if (k0) // 正確 { std::cout << "qwer" << std::endl; } else { std::cout << "zxcv" << std::endl; } return 0; }