web-dev-qa-db-ja.com

なぜ(int)xの代わりにstatic_cast <int>(x)を使うのですか?

static_cast関数はCスタイルまたは単純な関数スタイルのキャストよりも優先されるべきだと聞いたことがあります。これは本当ですか?どうして?

593
Tommy Herbert

主な理由は、古典的なCのキャストでは、static_cast<>()reinterpret_cast<>()const_cast<>()、およびdynamic_cast<>()と呼ばれるものが区別されていないことです。これら4つのことは完全に異なります。

static_cast<>()は通常安全です。言語に有効な変換、またはそれを可能にする適切なコンストラクタがあります。少し危険なのは、継承クラスにキャストダウンしたときだけです。あなたは、そのオブジェクトが実際にあなたがそれを主張する子孫であることを、言語の外部の手段によって(オブジェクトの中のフラグのように)確かめなければなりません。 dynamic_cast<>()は、結果がチェックされる(ポインタ)か、考えられる例外が考慮される(参照)限り、安全です。

一方、reinterpret_cast<>()(またはconst_cast<>())は常に危険です。あなたはコンパイラに言います:「私を信頼してください:これはfooのようには見えません(これは変更不可能ではないように見えますが)、それはそうです」。

最初の問題は、大きくて分散したコードを見ずにすべての規則を知らないと、Cスタイルのキャストでどれが発生するかを見分けることがほとんど不可能だということです。

これらを仮定しましょう:

class CMyClass : public CMyBase {...};
class CMyOtherStuff {...} ;

CMyBase  *pSomething; // filled somewhere

さて、これら二つは同じ方法でコンパイルされます。

CMyClass *pMyObject;
pMyObject = static_cast<CMyClass*>(pSomething); // Safe; as long as we checked

pMyObject = (CMyClass*)(pSomething); // Same as static_cast<>
                                     // Safe; as long as we checked
                                     // but harder to read

しかし、これとほぼ同じコードを見てみましょう。

CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert

pOther = (CMyOtherStuff*)(pSomething);            // No compiler error.
                                                  // Same as reinterpret_cast<>
                                                  // and it's wrong!!!

お分かりのように、関連するすべてのクラスについてよく知らずに2つの状況を区別する簡単な方法はありません。

2番目の問題は、Cスタイルのキャストを見つけるのが難しいということです。複雑な式では、Cスタイルのキャストを見るのは非常に困難です。本格的なC++コンパイラのフロントエンドなしでは、Cスタイルのキャストを探す必要のある自動化ツール(たとえば検索ツール)を書くことは事実上不可能です。一方、 "static_cast <"または "reinterpret_cast <"を検索するのは簡単です。

pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
      // No compiler error.
      // but the presence of a reinterpret_cast<> is 
      // like a Siren with Red Flashing Lights in your code.
      // The mere typing of it should cause you to feel VERY uncomfortable.

これはつまり、Cスタイルのキャストがより危険であるだけでなく、それらが正しいことを確認するためにそれらすべてを見つけるのははるかに難しいということです。

581
Euro Micelli

1つの実用的なヒント:あなたがプロジェクトを片付けようと思っているなら、あなたはソースコードのstatic_castキーワードを簡単に検索することができます。

108
Karl

要するに

  1. static_cast<>()はあなたにコンパイル時チェック能力を与えます、Cスタイルキャストはそうしません。
  2. static_cast<>()はC++ソースコード内のどこにでも簡単に見つけることができます。対照的に、C_Styleキャストは見つけるのが難しいです。
  3. 意図は、C++キャストを使用してはるかに良く伝えられます。

詳細説明

静的キャストは、 互換性のある型 の間の変換を実行します。これはCスタイルのキャストに似ていますが、より制限的です。たとえば、Cスタイルのキャストでは、整数ポインタでcharを指すことができます。

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

これは割り当てられたメモリの1バイトを指す4バイトのポインタになるので、このポインタへの書き込みはランタイムエラーを引き起こすか、または隣接するメモリを上書きするでしょう。

*p = 5; // run-time error: stack corruption

Cスタイルのキャストとは対照的に、静的キャストでは、ポインターとポインティング先のデータ型に互換性があることをコンパイラーが確認できます。これにより、プログラマーはコンパイル時にこの誤ったポインター割り当てを検出できます。

int *q = static_cast<int*>(&c); // compile-time error

続きを読む:
static_cast <>とCスタイルのキャストの違いは何ですか
そして
通常のキャスト対static_cast対dynamic_cast

68
Breeze

Cスタイルのキャストを使用すると異なることが起こるので、問題は単にstatic_castまたはCスタイルのキャストを使用するよりも大きいです。 C++キャスト演算子は、これらの操作をより明示的にすることを目的としています。

たとえば、ある値を別の値にキャストする場合、static_castとCスタイルのキャストは同じように見えます。

int i;
double d = (double)i;                  //C-style cast
double d2 = static_cast<double>( i );  //C++ cast

どちらも整数値をdoubleにキャストします。しかし、ポインタを使って作業すると、事態はさらに複雑になります。いくつかの例:

class A {};
class B : public A {};

A* a = new B;
B* b = (B*)a;                                  //(1) what is this supposed to do?

char* c = (char*)new int( 5 );                 //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error

この例(1)では、Aが指すオブジェクトは実際にはBのインスタンスであるため、おそらく問題ないでしょう。 (2)おそらく完全に合法です(整数の1バイトを見たいだけです)が、それはまた間違いであるかもしれませんその場合エラーは(3)のようにNiceになるでしょう。 C++キャスト演算子は、可能であればコンパイル時または実行時エラーを提供することによって、コード内でこれらの問題を明らかにすることを目的としています。

そのため、厳密な「値のキャスト」にはstatic_castを使用できます。実行時にポインタをポリモーフィックにキャストしたい場合はdynamic_castを使用してください。あなたが本当に型について忘れたいのなら、reintrepret_castを使うことができます。そしてconstをウィンドウの外に出すためにはconst_castがあります。

コードをより明示的にして、自分がしていることを知っているように見せかけるだけです。

27
Dusty Campbell

static_castは、誤ってconst_castまたはreinterpret_castを使用できないことを意味します。これは良いことです。

24
DrPizza
  1. Grepなどのツールを使用してコード内でキャストを簡単に見つけることができます。
  2. どのようなキャストを行っているのかを明示し、それを強制する際にコンパイラの助けを借りるようにします。あなたがconst-nessを捨て去りたいだけなら、あなたはそれからあなたが他のタイプの変換をすることを可能にしないconst_castを使うことができる。
  3. キャストは本質的に醜いです - プログラマーとしてのあなたは、コンパイラが通常あなたのコードをどのように扱うかを過大評価しています。あなたはコンパイラに「私はあなたよりも良く知っている」と言っています。そういうわけで、キャストを実行することはやや苦痛なことであるべきであり、そしてそれらは問題の原因である可能性があるのであなたのコードで突き出るべきであることは理にかなっています。

効果的なC++ はじめに

7
JohnMcG

それはあなたがどの程度タイプセーフティを課したいかということです。

(bar) foo(型変換演算子が提供されていない場合はreinterpret_cast<bar> fooと同等です)を書くときは、型の安全性を無視するようにコンパイラーに指示しています。

static_cast<bar> fooを書くとき、あなたはコンパイラに少なくとも型変換が理にかなっていることをチェックするように、そして整数型の場合は何らかの変換コードを挿入するように頼みます。


編集2014-02-26

私は5年以上前にこの答えを書きましたが、私はそれを間違っていました。 (コメント参照)しかし、それはまだ支持を得ています!

7
Pitarou

Cスタイルのキャストはコードブロックで見逃しやすいです。 C++スタイルのキャストは、より良い方法ではありません。彼らははるかに高い柔軟性を提供します。

reinterpret_castは整数からポインタ型への変換を可能にしますが、誤用されると安全ではありません。

static_castは数値型に対して適切な変換を提供します。列挙型から整数型、整数型から浮動小数点数、または型に自信があるデータ型まで。ランタイムチェックは実行されません。

一方、dynamic_castはこれらのチェックを実行して、あいまいな割り当てや変換にフラグを立てます。これはポインタと参照に対してのみ機能し、オーバーヘッドを招きます。

他にもいくつかありますが、これらはあなたが出会う主なものです。

4
Konrad

static_castは、クラスへのポインタを操作する以外に、クラスで明示的に定義されている変換や、基本型間の標準変換の実行にも使用できます。

double d = 3.14159265;
int    i = static_cast<int>(d);
3
prakash