2018年5月23日

[C++] static_cast & reinterpret_cast & C-style type cast

C 的型態轉換大家應該都很熟也很常用,不過在 C++ 中因為 C++ 的功能與語言特性複雜許多,C 語法的型態轉換一來不易辨識,二來其內部運作方式較為隱晦不清,因此 C++ 引入了四種型態轉換運算子,分別是
static_cast, dynamic_cast, const_cast 與 reinterpret_cast
大多數情況我們所使用的 C 型別轉換通常是對應到 static_cast;dynamic_cast 則是應用到繼承體系的型別上;const_cast 其作用只在於拔掉變數的常數性 (const) 與揮發性 (volatile),其餘維持不變;其中最玄的該數 reinterpret_cast 了,有部分 C 的型態轉換的使用情形對應過來 C++ 這邊其實是要用 reinterpret_cast

其實 More Effective C++ 的 Item 2 有說明在使用 C++ 時他建議使用 C++ 的這四種轉型運算子,而不要去使用 C 的轉型運算子,即便這四種運算子看起來又臭又長,但是他們的功能性與限制都明確許多,不易誤用 (因為用錯 compiler 就會叫給你聽 XD) 這次遇上的問題就是我以為是 static_cast 會做的事情其實是 reinterpret_cast 在做的 Orz


dynamic_cast 跟 const_cast 因為職責跟使用範圍太過侷限且明確,誤用的狀況應該是低很多,這次篇先把重心放在 static_cast 跟 reinterpret_cast。

char c = 'c';
unsigned char uc = static_cast<unsigned char>(c); // OK

char* cptr = nullptr;
unsigned char* ucptr = static_cast<unsigned char*>(cptr); // Error!

void* vptr = static_cast<void*>(cptr); // OK

這邊列的是兩種常用的型態轉換情形,primitive type 轉成 primitive type,pointer to primitive type 轉成 pointer to primitive type。第一種狀況沒啥問題,問題出在第二種情況:這邊不能用 static_cast 只能用 reinterpret_cast。唯一的例外情況就是轉型成 void*,這是因為 C++ 標準上直接明定說這是任何指標都能正確轉型的型態,另一種絕對正確的指標轉型就是從 void* 轉回原本的指標型態。

好啦,所以問題回到為什麼這邊不能用 static_cast 而只能用 reinterpret_cast? 從一種 pointer type 轉型成另一種 pointer type 一般來說想做的事情就是用不同的型別來解讀相同的 bit pattern,以上面的例子來說,就是用 unsigned char 去解讀被 cptr 指到的區塊,而正這是 reinterpret_cast 要做的事情。static_cast 在這情況不能用的原因應該就是為了不搶掉 reinterpret_cast 的職責 compiler 才報錯誤訊息,而 void* 是標準中直接講明允許這種轉換,而且轉型成 void* 也沒有解讀 bit pattern 的用意,所以這情況才會允許使用 static_cast 吧。

4 則留言:

  1. 想請問
    char c;
    reinterpret_cast(c);
    上面這段 code 為何不能用 reinterpret_cast 呢?
    是因為如果 static_cast 就可以做到的轉換就不能也不該用 reinterpret_cast 嗎?
    感謝~~

    回覆刪除
    回覆
    1. char c;
      reinterpret_cast(c);
      抱歉應該是這樣才對

      刪除
    2. 怕爆 這個留言是不是會 escape 特殊字元XD
      我是想說用 reinterpret_cast 把 char 轉成 int

      刪除
    3. 可以參考下面這篇的第 6 點:
      https://en.cppreference.com/w/cpp/language/reinterpret_cast

      可以編譯的寫法是要轉型成 int& 而不是 int (把 c 當成 lvalue expression)
      至於原因跟 static_cast 做不做得到其實沒有關連性

      刪除