C++ 运算符重载(二) | 类型转换运算符,二义性问题
文章目錄
- 類型轉(zhuǎn)換運算符
- 概念
- 避免過度使用類型轉(zhuǎn)換函數(shù)
- 解決上述問題的方法
- 轉(zhuǎn)換為 bool
- 顯式的類型轉(zhuǎn)換運算符
- 類型轉(zhuǎn)換二義性
- 重載函數(shù)與類型轉(zhuǎn)換結合導致的二義性
- 重載運算符與類型轉(zhuǎn)換結合導致的二義性
類型轉(zhuǎn)換運算符
概念
類型轉(zhuǎn)換運算符(conversion operator)是類的一種特殊成員函數(shù)。負責將一個類類型的值轉(zhuǎn)換成其他類型。
operator type() const ;其中 type 表示某種類型。類型轉(zhuǎn)換運算符可以面向任意類型(除了 void 之外)進行定義,只要該類型能作為函數(shù)的返回類型。因此,我們不允許轉(zhuǎn)換成數(shù)組或者函數(shù)類型,但允許轉(zhuǎn)換成指針(包括數(shù)組指針及函數(shù)指針)或者引用類型。
一個類型轉(zhuǎn)換函數(shù)必須是類的成員函數(shù);它不能聲明返回類型,形參列表也必須為空。類型轉(zhuǎn)換函數(shù)通常不應該改變待轉(zhuǎn)換對象的內(nèi)容,因此,應該是const。
運用實例,定義一個簡單的類,令其表示 0~255 之間的一個整數(shù):
構造函數(shù)將算術類型的值轉(zhuǎn)換成 SmallInt 對象,而類型轉(zhuǎn)換運算符將 SmallInt 對象轉(zhuǎn)換成 int:
盡管編譯器一次只能執(zhí)行一個 我們定義的類型轉(zhuǎn)換(如上面的構造函數(shù)/類型轉(zhuǎn)換運算符),但可以將其搭配 內(nèi)置類型轉(zhuǎn)換(如double可以轉(zhuǎn)換成int) 實現(xiàn)二次轉(zhuǎn)換。
// 內(nèi)置類型轉(zhuǎn)換將 doulbe 實參轉(zhuǎn)換成 int SmallInt si = 3.14; // 調(diào)用 SmallInt(int) 構造函數(shù),然后調(diào)用拷貝構造函數(shù) // SmallInt 的類型轉(zhuǎn)換運算符將 si 轉(zhuǎn)換成 int si + 3.14; // 內(nèi)置類型將所得的 int 繼續(xù)轉(zhuǎn)換成 double盡管類型轉(zhuǎn)換函數(shù)不負責指定返回類型,但實際上每個類型轉(zhuǎn)換函數(shù)都會返回一個對應類型的值:
避免過度使用類型轉(zhuǎn)換函數(shù)
例如,假設某個類表示 Date,我們也許會為它添加一個從 Date 到 int 的轉(zhuǎn)換。然而,類型轉(zhuǎn)換函數(shù)的返回值應該是什么?
- 一種可能的解釋是,函數(shù)返回一個十進制數(shù),依次表示年、月、日,例如,July 30,1989 可能轉(zhuǎn)換為 int 值 19890730。
- 同時還存在另外一種合理的解釋,即類型轉(zhuǎn)換運算符返回的 int 表示的是從某個時間節(jié)點(比如 January 1,1970)開始經(jīng)過的天數(shù)。
問題在于 Date 類型的對象和 int 類型的值之間不存在明確的一對一映射關系。因此在此例中,不定義該類型轉(zhuǎn)換運算符也許會更好。作為替代的手段,類可以定義一個或多個普通的成員函數(shù)以從各種不同形式中提取所需的信息。
對于類來說,定義向 bool 的類型轉(zhuǎn)換還是比較普遍的現(xiàn)象。
int i = 42; cin << i; // 如果向 bool 的類型轉(zhuǎn)換不是顯式的,則該代碼在編譯器看來是合法的因為 istream 本身并沒有定義 <<,所以本來代碼應該產(chǎn)生錯誤。然而,該代碼能使用 istream 的 bool類型轉(zhuǎn)換運算符 將 cin 轉(zhuǎn)換成 bool ,而這個 bool值 接著會被提升成 int 并用作內(nèi)置的左移運算符的左側運算對象。這樣一來,提升后的 bool值(1或0) 最終會 被左移42個位置。 這一結果顯然與我們的預期大相徑庭。
解決上述問題的方法
轉(zhuǎn)換為 bool
- 標準庫的早期版本中,IO 類型定義了向 void* 的轉(zhuǎn)換規(guī)則,以求避免上述問題。
- 在 C++11 標準中,IO 標準庫通過定義一個向 bool 的顯式類型轉(zhuǎn)換實現(xiàn)同樣的目的。
其實我們在編程中經(jīng)常用到 IO 類型定義的 operator bool :
while(std::cin >> value)為了對條件求值,cin 被 istream operator bool 類型轉(zhuǎn)換函數(shù)隱式地執(zhí)行了轉(zhuǎn)換。如果 cin 的條件狀態(tài)是 good,則該函數(shù)返回為真;否則該函數(shù)返回為假。(這部分知識可以看我之前的博客)
向 bool 的類型轉(zhuǎn)換通常用在條件部分,因此 operator bool 一般定義成 explicit 的。
顯式的類型轉(zhuǎn)換運算符
為了防止上面第二點這樣的異常情況發(fā)生,我們可以使用 explicit 關鍵字。
當類型轉(zhuǎn)換運算符是顯式的時,我們也能執(zhí)行類型轉(zhuǎn)換,不過必須通過顯式的強制類型轉(zhuǎn)換才可以。
該規(guī)定存在一個例外,即,如果表達式被用作條件,則編譯器會將顯式的類型轉(zhuǎn)換自動應用于它。 換句話說,當表達式出現(xiàn)在下列位置時,顯式的類型轉(zhuǎn)換將被隱式地執(zhí)行:
- if 、while 及 do 語句的條件部分
- for 語句頭的條件表達式
- 邏輯非運算符(!)、邏輯或運算符(||)、邏輯與運算符(&&)的運算對象
- 條件運算符(? :)的條件表達式。
類型轉(zhuǎn)換二義性
如果類中包含一個或多個類型轉(zhuǎn)換,則必須確保在類類型和目標類型之間只存在唯一一種轉(zhuǎn)換方式。否則的話,我們編寫的代碼將很可能會具有二義性。
在兩種情況下可能產(chǎn)生多重轉(zhuǎn)換路徑:
通常情況下,不要為類定義相同的類型轉(zhuǎn)換,也不要在類中定義兩個及兩個以上轉(zhuǎn)換源或轉(zhuǎn)換目標是算術類型的轉(zhuǎn)換。
第一種情況舉例:
解決方法是顯式調(diào)用:
A a1 = f(b.operator A()); A a2 = f(A(b));第二種情況舉例:
我們使用兩個用戶定義的類型轉(zhuǎn)換時,如果轉(zhuǎn)換函數(shù)之前或之后存在標準類型轉(zhuǎn)換,則標準類型轉(zhuǎn)換將決定最佳匹配到底是哪個:
重載函數(shù)與類型轉(zhuǎn)換結合導致的二義性
有時會出現(xiàn)這種情況:
或這種情況:
雖然我們可以通過顯式地構造正確的類型而消除二義性:
manip(C(10)); // 調(diào)用 manip(const C&) manip2(E(double(10))); // 調(diào)用 manip2(const E&)但意味著程序的設計存在不足。
重載運算符與類型轉(zhuǎn)換結合導致的二義性
重載的運算符也是重載的函數(shù)。因此也遵從通用的函數(shù)匹配規(guī)則。例如,如果 a 是一種類類型,則表達式 a sym b 可能是:
a.operatorsym(b); // a 有一個 operatorsym 成員函數(shù) operatorsym(a, b); // operatorsym 是一個普通函數(shù)和普通函數(shù)不同,我們無法通過調(diào)用的形式區(qū)分當前調(diào)用的是成員函數(shù)還是非成員函數(shù)。
舉個例子:
- 第一條加法語句接受兩個 SmallInt 值并執(zhí)行 + 運算符的重載版本。
- 第二條加法語句具有二義性:因為我們可以把 0 轉(zhuǎn)換成 SmallInt,然后使用 SmallInt 的 +;或者把 s3 轉(zhuǎn)換成 int,然后對于兩個 int 執(zhí)行內(nèi)置的加法運算。
如果我們對同一個類既提供了轉(zhuǎn)換目標是算術類型的類型轉(zhuǎn)換,也提供了重載的運算符,則將會遇到重載運算符與內(nèi)置運算符的二義性問題。
總結
以上是生活随笔為你收集整理的C++ 运算符重载(二) | 类型转换运算符,二义性问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 「常识」鈊字五行属什么
- 下一篇: 网易校园招聘历年经典面试题汇总:C++研