44358


`c++`で複数あるキャストの違いを教えてください。  
どちらも使える場合はどちらを選べばいいかわかまりません。

[code]

    static_cast<const char*>(x); // 静的キャスト(静的な普通の型変換を行うキャスト) 
    (const char*)x;              // キャスト的記法 () 
    const char*(x);              // 関数的記法
    
[/code]


CC BY-SA 4.0
2018-05-25T11:20:05.047
44260
2018-06-04T05:23:52.757
2018-05-25T17:49:08.937
3060
question
c++
複数あるキャスト記法をどのように使い分ければいいか教えてください


原則としては `static_cast` を使ってください。

キャストにはいくつかの種類があって、何の問題もない安全なものも、正しく使えば安全なものも、何も保証されないものもあります。 しかし `(const
char*)` という記法は区別なくキャストしてしまうのです。 (関数風の書き方でも効果は同じです。)

`static_cast` は `static_cast` の要件を満たす変換のみしか許されません。 そして `static_cast`
が許す変換は比較的安全なものに限られています。 もしうっかり危険な変換をしようとしてしまったらエラーになってくれるので安心です。

逆に、危険なキャストは危険なキャストとしてそれぞれの性質に応じて `dynamic_cast`, `const_cast`,
`reinterpret_cast` を使い分けるべきです。 古いスタイルのキャストは C
との互換性のために残されていますが、どのような意図をもってキャストしているのか読み取り難く、バグの原因になりやすいので原則として避けるのが望ましいと考えられています。


CC BY-SA 4.0
2018-05-25T14:14:29.547
44263
2018-05-25T14:14:29.547
3364
44260
answer


[c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示") ソースでも
[c](/questions/tagged/c "'c' のタグが付いた質問を表示") ソースでも共通に使うヘッダファイル等ではしかたないので
c-style キャスト `(type)value` を使う必要があります。ですが [c++](/questions/tagged/c%2b%2b
"'c++' のタグが付いた質問を表示") 限定の場合は常に [c++](/questions/tagged/c%2b%2b "'c++'
のタグが付いた質問を表示") で新設されたキャストを使うようにするとソースコード自体にプログラマの意図を表現できて幸せになれます。

言語仕様書的な厳密話はちょっとおいておいて実用上の話をすると

  * static_cast 

値を、違う型の｢意味的に同じ」（内部表現が異なる）値にします。例えば  
`1` (`int` 型) `1L` (`long` 型) `1.0f` (`float` 型) `1.0` (`double` 型)
は意味的には同じ数値ですが、コンピュータの中での内部表現は異なることが普通です。この種の変換には `static_cast`
を使うと良いとされています。一般に安全な型変換です。

[code]

    double average(int x, int y) {
      return (static_cast<double>(x) + static_cast<double>(y))/2.0;
    }
    
[/code]

`static_cast` は型変換コンストラクタの起動であると知っている人は次のように書くかもしれません。特に `class`
型に変換したい場合にはこっちの方が自然かも。

[code]

    return (double(x)+double(y))/2.0;
    
[/code]

オイラも今回調べていて初めて知ったのですが、この形式を関数型キャストとか関数スタイルキャストとか言う場合があるらしいです。少なくともオイラが
[c++03](/questions/tagged/c%2b%2b03 "'c++03' のタグが付いた質問を表示")
言語仕様書を読んだ範囲でそのような文言を見たことはありませんのでどこかの方言なのかもしれません。

<https://www.ibm.com/support/knowledgecenter/ja/ssw_ibm_i_71/rzarg/cast_operator.htm>

[c++03](/questions/tagged/c%2b%2b03 "'c++03' のタグが付いた質問を表示")
仕様書的にはこれはあくまで一時オブジェクトを型変換コンストラクタを用いて直接初期化すると解釈されます。

[code]

    double d=double(0); // OK コンストラクタの呼び出し（組み込み型でも可能）
    char* p=char *(0);  // NG コンストラクタの呼び出しと解釈できない
    typedef void* vp_type; // 単純型指定子→括弧で囲まれた式並び、ならよいので
    vp_type q=vp_type(0);  // typedef でつけた別名でも OK
    
[/code]

(add)  
もう少し精読してみたところ 5.2 後置式の 5.2.3 明示的型変換(関数的記法) というのがあるのであながち方言でもないのでしょう。 5.2.3
によると引数が１つのときはキャストと同等で、２つ以上の場合は適切なコンストラクタのあるクラスであって後略とあります。引数の数に関係なく既に書いたとおり一時オブジェクトの構築です。

  * reinterpret_cast

値の内部表現を維持したまま、違う型と解釈します。型変換でなく型解釈変更というか、そういう場合に限定して使います。どういうことかというと、例えば組み込み系とかでよくあるのですが

メモリ上 `0x00101280` 番地には周辺回路Ａのレジスタがあって、この番地をアクセスすると回路Ａを働かせることができる、といった場合に
`*reinterpret_cast<volatile uint16_t*>(0x00101280)` などと記述します。長ったらしいので
`#define` 等で別名をつけたりすることが多いです。

[code]

    #define A_REG *reinterpret_cast<volatile uint16_t*>(0x00101280)
    
[/code]

すると `A_REG=0x1234;` なり `uint16_t rxd=A_REG;` と書けて、これで周辺回路をアクセスすることができます。

この例では一つの値 `0x00101280`
があって、型解釈だけを整数型からポインタ型に変更していることになります。組み込み系以外で現れる例では無関係な（派生関係になくて互いに独立した、と言う意味）型
`a_type` と `b_type` があるとき `a_type*` を `b_type*`
に型解釈を変更することでしょう。このような変換は一般的にはできない危険な行為です。 `reinterpret_cast`
を明示するということは、プログラマが（今のこの処理系においては）それができることを熟知していて、結果に責任を負う、というソースコード上の意思表明と読むことができます。

  * const_cast

ポインタまたは参照の cv-ness 脱着の目的にのみ使います。 `const`
を追加する側は普通は安全であり、暗黙変換で可能なので明示しないことが多いです。 `const_cast` を書くことは、プログラマが constness
を除去する（および引き続き非 const アクセスを行っている）つまり危険な行為をしているが、承知の上であることを明示する目的につかわれます。

※オブジェクト自体の constness の脱着はできません。  
※もともと `const` なオブジェクトを非 `const` アクセスするとバグるので注意が必要です。

[code]

    void badexample(const int* p) {
        // ポインタの const を除去している
        *const_cast<int*>(p)=0;
    }
    void test() {
        int a;
        badexample(&a); // 文法上は妥当（良いコードとはいえないが)
        const int b= -1;
        badexample(&b); // 不当アクセス
    }
    
[/code]

c-style キャスト `(type)value`
は、ここまでの３つの機能を全て区別しないで行うので、プログラマの意思表明「危険なことは承知の上でやっている」と読めません。
[c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示") では使わない方向で行きたいところです。

* * *

  * dynamic_cast

ソースコード上は型変換に見えますが、実際は実行時の「動的型判別」です。 c-style キャストでは `dynamic_cast`
と同じことができません。詳細解説は略。ライブラリやフレームワークの都合で使う場合はしかたないところがあります。ですが 100% 自作しているコード内で
`dynamic_cast` を使う羽目になったら、設計か実装かどこかに瑕疵があります。 `dynamic_cast`
を書く必要が生じたらリファクタリングしましょう。

* * *

`static_cast` と `reinterpret_cast`
の振る舞いが異なる別例を挙げておきます。実行する前にどうなるのか脳内で検討してみましょう（結果および解説略）。

[code]

    #include <iostream>
    struct a { virtual ~a() { } };
    struct b { virtual ~b() { } };
    struct d : a, b { };
    int main() {
        d x;
        std::cout << static_cast<a*>(&x) << std::endl;
        std::cout << static_cast<b*>(&x) << std::endl;
        std::cout << reinterpret_cast<a*>(&x) << std::endl;
        std::cout << reinterpret_cast<b*>(&x) << std::endl;
    }
    
[/code]


CC BY-SA 4.0
2018-05-29T07:23:02.697
44358
2018-06-04T05:23:52.757
2018-06-04T05:23:52.757
8589
8589
44260
answer