(int)xの代わりにstatic_cast <int>(x)を使用する理由


667

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


36
あなたの名誉に異議を唱え、質問し、答えてください
Graeme Perrow、

25
私は同意しません。この他の質問は、C ++で導入されたキャスト間の違いを説明することに関するものでした。この質問は、わずかに異なるstatic_castの実際の有用性についてです。
ヴィンセントロバート

2
確かに2つの質問をマージすることもできますが、このスレッドから保持する必要があるのは、Cスタイルのキャストよりも関数を使用することの利点です。現在、他のスレッドの1行の回答でのみ言及されており、投票はありません。 。
トミーハーバート

6
この質問は、intなどの「組み込み」型に関するものですが、その質問はクラス型に関するものです。これは、個別の説明に値するほどの重要な違いのようです。

9
static_castは、実際には関数ではなく演算子です。
ThomasMcLeod 2013

回答:


633

主な理由は、古典的なCのキャストたちが呼んでいるものの区別をしないということですstatic_cast<>()reinterpret_cast<>()const_cast<>()、とdynamic_cast<>()。これらの4つは完全に異なります。

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

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

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

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

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

CMyBase  *pSomething; // filled somewhere

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

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

pMyObject = (CDerivedClass*)(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スタイルのキャストの方が危険であるだけでなく、すべてを見つけて正しいことを確認するのが非常に困難です。


30
static_cast継承階層のキャストダウンには使用しないでくださいdynamic_cast。これは、nullポインターまたは有効なポインターを返します。
David Thornley

41
@David Thornley:通常はそう思います。static_castそのような状況で使用する際の注意点を示したと思います。dynamic_cast安全かもしれませんが、それが常に最良の選択肢であるとは限りません。時々あなたは知っていますポインターがコンパイラーに対して不透明であることにより、特定のサブタイプを指しているているあり、aの方static_castが高速です。少なくとも一部の環境でdynamic_castは、オプションのコンパイラサポートとランタイムコスト(RTTIを有効にする)が必要であり、自分で実行できるいくつかのチェックのためだけに有効にしたくない場合があります。C ++のRTTIは、この問題に対する唯一の可能な解決策です。
Euro Micelli、2010年

18
Cキャストに関するあなたの主張は誤りです。すべてのCキャストは値変換であり、C ++にほぼ匹敵しますstatic_cast。Cと同等のreinterpret_castisは*(destination_type *)&、つまり、オブジェクトのアドレスを取得し、そのアドレスを別の型へのポインターにキャストしてから、逆参照します。Cは、この構築物の挙動を定義するための文字タイプまたは特定の構造体タイプの場合を除き、それは一般的にCの未定義の動作につながる
R .. GitHubのSTOPはICE手助け

11
あなたの良い答えは投稿の本文に対処します。「なぜ(int)xではなくstatic_cast <int>(x)を使用するのか」というタイトルへの回答を探していました。つまり、タイプint(およびint単独)の場合、なぜ使用するのかstatic_cast<int> vs。(int)を、クラス変数とポインターをすることだけにあるようです。これについて詳しく説明してください。
chux-モニカを2013年

31
@chux、int dynamic_cast適用されませんが、他のすべての理由が立っています。たとえば:みましょうと言うには、v関数のパラメータは、として宣言されfloat、その後、(int)vですstatic_cast<int>(v)。あなたがにパラメータを変更した場合でもfloat*(int)v静かになりreinterpret_cast<int>(v)ながら、static_cast<int>(v)違法であり、正しくコンパイラによってキャッチ。
Euro Micelli、2013

115

実用的なヒントの1つ:プロジェクトを片付ける場合は、ソースコードでstatic_castキーワードを簡単に検索できます。


3
"(int)"のように角かっこを使用して検索することもできますが、C ++スタイルのキャストを使用するのに適切な理由と正当な理由があります。
Mike

4
偽陽性を検出する@Mike-単一のintパラメーターを持つ関数宣言。
Nathan Osman

1
これは誤検出を引き起こす可能性があります。自分だけが作成者ではないコードベースを検索している場合、何らかの理由で他の人が導入したCスタイルのキャストを見つけることができません。
ルスラン

7
これを行うと、プロジェクトを片付けるのにどのように役立ちますか?
Bilow

static_castは正しいものである可能性が高いため、検索しないでください。再設計できる場所を示すため、reinterpret_cast、const_cast、および場合によってはdynamic_castを検索しながら、static_castを除外します。Cキャストはすべてが混在しているため、キャストする理由がわかりません。
Dragan

78

つまり

  1. static_cast<>() コンパイル時間チェック機能を提供しますが、C-Styleキャストは提供しません。
  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 の違いは何ですか


21
static_cast<>()もっと読みやすいとは思いません。つまり、時々そうですが、ほとんどの場合、特に基本的な整数型では、それは恐ろしく、不必要に冗長です。例:これは、32ビットワードのバイトをスワップする関数です。static_cast<uint##>()キャストを使用して読むことはほとんど不可能ですが、(uint##)キャストを使用して理解することは非常に簡単です。コードの画像: imgur.com/NoHbGve
Todd Lehman

3
@ToddLehman:ありがとう、alwaysでも私はどちらも言わなかった。(しかし、ほとんどの場合はそうです)cスタイルのキャストの方がずっと読みやすい場合があります。それが、cスタイルのキャストがまだライブでc ++ imhoを開始している理由の1つです。:)ちなみに、それはとても良い例でした
Rika

8
その画像の@ToddLehmanコードは、2つのキャストチェーン((uint32_t)(uint8_t))を使用して、最下位以外のバイトがリセットされるようにします。そのため、ビットごとの(0xFF &)があります。キャストの使用は意図を難読化しています。
OO Tiib

28

Cスタイルのキャストを使用するとさまざまなことが起こるため、静的なキャストや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のインスタンスなので、おそらくOKです。しかし、コードのその時点で実際に指しているものがわからない場合はどうでしょうか。(2)おそらく完全に合法です(整数の1バイトのみを確認する必要があります)が、(3)のようにエラーが発生した場合にエラーになることもあります。C ++キャスト演算子は、可能な場合にコンパイル時エラーまたは実行時エラーを提供することにより、コードでこれらの問題を公開することを目的としています。

したがって、厳密な「値のキャスト」には、static_castを使用できます。実行時にポインタのポリモーフィックキャストが必要な場合は、dynamic_castを使用します。タイプを忘れたい場合は、reintrepret_castを使用できます。そして、単にconstをウィンドウから投げ出すためにconst_castがあります。

彼らはあなたが何をしていたかあなたが知っているように見えるように、コードをより明確にするだけです。


26

static_cast誤ってconst_castor できないことを意味しますreinterpret_cast。これは良いことです。


4
Cスタイルのキャストに対する追加の(どちらかといえばマイナーではありますが)利点は、それがより際立っており(潜在的に悪いことを行うと醜く見えるはずです)、よりグレップ可能です。
マイケル・バー、

4
私の本では、grep機能は常にプラスです。
ブラナン2008

7
  1. grepなどのツールを使用して、キャストをコード内で簡単に見つけることができます。
  2. 実行しているキャストの種類を明示し、コンパイラーの助けを借りてそれを強制します。const-nessだけをキャストしたい場合は、const_castを使用できます。これにより、他のタイプの変換を行うことができなくなります。
  3. キャストは本質的に醜いです-プログラマーとして、あなたはコンパイラーが通常あなたのコードをどのように扱うかを覆します。あなたは編集者に「私はあなたよりもよく知っている」と言っています。そのため、キャストを実行するのはやや苦痛なことであり、問​​題の原因である可能性があるため、コード内でキャストする必要があります。

効果的なC ++の概要を参照してください


クラスについてはこれに完全に同意しますが、C ++スタイルのキャストをPODタイプに使用しても意味がありますか?
ザカリークラウス14

私はそう思う。3つの理由はすべてPODに当てはまります。クラスとPODに個別のルールを設定するのではなく、1つのルールだけを設定すると便利です。
JohnMcG 2014

興味深いことに、将来のPODタイプのコードでキャストの方法を変更する必要があるかもしれません。
ザカリークラウス2014

7

それはあなたが課したいどれだけのタイプセーフについてです。

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

あなたが書くとき static_cast<bar> foo、少なくとも型変換が意味があることを確認し、整数型の場合は変換コードを挿入するようコンパイラーに要求します。


編集2014-02-26

私は5年以上前にこの回答を書きましたが、間違っていました。(コメントを参照してください。)しかし、それでも賛成票が入ります!


8
(bar)fooはreinterpret_cast <bar>(foo)と同等ではありません。「(TYPE)expr」のルールは、使用する適切なC ++スタイルのキャストを選択することです。これには、reinterpret_castが含まれる場合があります。
リチャードコーデン

いい視点ね。Euro Micelliはこの質問に対して決定的な答えを出しました。
ピタロウ2008

1
また、static_cast<bar>(foo)括弧付きです。も同じですreinterpret_cast<bar>(foo)
LF

6

Cスタイルのキャストは、コードのブロックで見逃しがちです。C ++スタイルのキャストは、優れた実践方法であるだけではありません。はるかに高い柔軟性を提供します。

reinterpret_castは、ポインター型変換への統合を許可しますが、誤用すると危険な場合があります。

static_castは、数値型(enumからintやintからfloatへの変換、または型に自信のある任意のデータ型)を適切に変換します。ランタイムチェックは実行されません。

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

他にもいくつかありますが、これらはあなたが遭遇する主なものです。


4

static_castは、クラスへのポインタの操作の他に、クラスで明示的に定義された変換を実行したり、基本的な型間の標準変換を実行したりするためにも使用できます。

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

4
static_cast<int>(d)しかし、なぜ(int)dもっと簡潔で読みやすいのに、誰もがと書くのでしょうか?(つまり、オブジェクトポインターではなく、基本型の場合です。)
Todd Lehman

@ gd1 —なぜ誰もが読みやすさよりも一貫性を重視するのでしょうか?(実際には半分深刻)
トッドリーマン

2
@ToddLehman:私は、特定のタイプが何らかの形であなたにとって特別であるという理由だけで例外を設けることは、私には意味をなさないと考え、また、あなたの読みやすさの概念にも同意しません。別のコメントで投稿した画像からわかるように、短いほど読みやすくなるわけではありません。
gd1 2016年

2
static_castは、特定の種類の変換を行うという明確で意識的な決定です。したがって、意図が明確になります。また、コードレビュー、バグ、アップグレードの練習でソースファイルから変換を検索するためのマーカーとしても非常に便利です。
Persixty

1
@ToddLehman対位法:なぜだろう誰の書き込み(int)d時には、int{d}以前よりもずっと読みやすいのですか?コンストラクター、またはのように関数のように()、構文は、複雑な式の括弧の悪夢のような迷路に展開するのにそれほど高速ではありません。この場合、のint i{d}代わりになりint i = (int)dます。はるかに優れたIMO。とは言っても、式で一時的なものだけが必要な場合は、static_castコンストラクタキャストを使用したことがないので、考えていません。私は(C)casts急いでデバッグcouts ...を作成するときにのみ使用します
underscore_d
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.