「void *」がC ++で暗黙的にキャストされないのはなぜですか?


30

Cでは、void *他のポインター型にキャストする必要はなく、常に安全に昇格されます。ただし、C ++では、そうではありません。例えば、

int *a = malloc(sizeof(int));

Cでは動作しますが、C ++では動作しません。(注:私はあなたがmallocC ++で、またはその点newで使用すべきではないことを知っています、代わりにスマートポインタおよび/またはSTLを好む必要があります;これは純粋に好奇心から尋ねられます)C ++標準はこの暗黙的なキャストを許可しないのはなぜですか、 C標準はそうですか?


3
long *a = malloc(sizeof(int));おっと、誰かが1つのタイプだけを変更するのを忘れていました!
ドーバル

4
@Doval:これは、sizeof(*a)代わりに使用することで簡単に修正できます。
wolfPack88

3
私はratchetfreakのポイントは、理由Cがあるため、この暗黙の型変換がないことである@信じてmalloc割り当てられたタイプへのポインタを返すことはできません。 newC ++は割り当てられた型へのポインタを返すので、適切に記述されたC ++コードにはvoid *キャストするs がありません。
ロボット

3
余談ですが、他のポインタ型ではありません。データポインターのみを適用する必要があります。

3
@ wolfPack88あなたの歴史は間違っています。C ++にはありましたがvoid、Cにはありませんでした。そのキーワード/アイデアがCに追加されたとき、Cのニーズに合わせ変更しました。それは、ポインタ型がまったくチェックさ始めた直後です。あなたがオンラインK&R Cの説明パンフレットを見つけることができるかどうかを確認し、またはウェイトグループのようなCプログラミングテキストのヴィンテージコピーC入門。ANSI CはC ++からバックポートまたはインスパイアされた機能でいっぱいで、K&R Cははるかに簡単でした。そのため、C ++が当時存在していたCを拡張し、ご存知のCをC ++から削除した方が正確です。
JDługosz

回答:


39

暗黙的な型変換は通常安全ではないため、C ++はCよりも安全な型付けを行うためです。

ほとんどの場合、変換がエラーである場合でも、Cは通常、暗黙的な変換を許可します。これは、Cがプログラマーが何をしているのかを正確に知っていると仮定し、そうでない場合は、コンパイラーの問題ではなく、プログラマーの問題だからです。

C ++は通常、潜在的にエラーの可能性があるものを許可せず、型キャストを使用して明示的に意図を述べる必要があります。これは、C ++がプログラマーフレンドリーになろうとしているためです。

実際にもっと入力する必要があるのに、どうして親しみやすいのかと尋ねるかもしれません。

さて、あなたは、任意のプログラム、任意のプログラミング言語の任意のコード行が、通常、書かれるよりも何度も読まれることを知っています(*)。したがって、読みやすさは、書きやすさよりもはるかに重要です。また、読み取り時に、明示的な型キャストによって潜在的に安全でない変換を目立たせることは、何が起こっているのかを理解し、何が起こっているのかが実際に意図されたものであるという一定の確実性を持つのに役立ちます。

さらに、明示的なキャストを入力しなければならないという不便さは、警告されたかもしれないが、決して警告されなかった誤った割り当てによって引き起こされたバグを見つけるために何時間もトラブルシューティングを行う不便さに比べて些細なことです。

(*)理想的には一度だけ書かれますが、再利用の適性を判断するために誰かがそれをレビューする必要があるたびに、そしてトラブルシューティングが行われるたびに、そして誰かがその近くにコードを追加する必要があるたびに読まれます、そして、毎回近くのコードのトラブルシューティングなどがあります。これは、「一度だけ書き込み、実行してから捨てる」スクリプトを除くすべての場合に当てはまるため、ほとんどのスクリプト言語には、読みやすさを完全に無視して書きやすさを促進する構文があるのも不思議ではありません。perlが完全に理解できないと思ったことはありませんか?あなた一人じゃありません。そのような言語を「書き込み専用」言語と考えてください。


Cは本質的にマシンコードの1ステップ上にあります。このようなことをCに許してもらえます。
Qix

8
プログラム(言語が何であれ)は読むことを意図しています。明示的な操作が際立っています。
マチューM.

10
特定のOOP機能がC ++に実装されている方法では、同じオブジェクトへのポインターがポインターの種類に応じて異なる値を持つ可能性があるため、C ++ではキャストスルーの方が安全でvoid*はないことに注意してください。
ハイド

なつみ 本当です。それを追加してくれてありがとう、答えの一部である価値がある。
マイクナキス

1
@MatthieuM .:ああ、しかし、あなたは本当にすべてを明示的にする必要はありません。読みやすくすることで読みやすさが向上することはありません。この時点では、バランスは明確であることは明らかです。

28

Stroustrupが言うことは次のとおりです。

Cでは、暗黙的にvoid *をT *に変換できます。これは安全ではありません

その後、彼はvoid *がどのように危険であるかの例を示し、次のように述べています。

...したがって、C ++では、void *からT *を取得するには、明示的なキャストが必要です。...

最後に、彼は次のように述べています。

Cでのこの安全でない変換の最も一般的な使用法の1つは、malloc()の結果を適切なポインターに割り当てることです。例えば:

int * p = malloc(sizeof(int));

C ++では、typesafe new演算子を使用します。

int * p = new int;

彼は、このことについて、「C ++の設計と進化」でさらに詳しく説明しています

そのため、答えは次のように要約されます。言語設計者は、それが安全でないパターンであると信じ、それを違法にし、パターンが通常使用される目的を達成する別の方法を提供しました。


1
このmalloc例に関してmallocは、コンストラクターを(明らかに)呼び出さないことに注意する価値があります。したがって、それがメモリを割り当てるクラス型である場合、クラス型に暗黙的にキャストできると誤解を招く可能性があります。
chbaker0

これは非常に良い答えであり、おそらく私のものよりも優れています。私は「権威からの主張」アプローチを少しだけ嫌います。
マイクナキス

「新しいint」-うわー、C ++初心者として、ヒープに基本型を追加する方法を考えるのに苦労していましたが、それができるとは知りませんでした。-1はmallocに使用します。
Katana314

3
@MikeNakisFWIW、私の意図はあなたの答えを補完することでした。この場合、質問は「なぜ彼らがそれをしたのか」だったので、チーフデザイナーからの連絡は正当であると考えました。
ロボット

11

Cでは、void *を他のポインター型にキャストする必要はなく、常に安全に昇格されます。

はい、常に宣伝されていますが、安全ではありません。

C ++は、Cよりも安全な型システムを使用しようとするため、この動作を無効にします。この動作は安全ではありません


一般に、型変換に対する次の3つのアプローチを検討してください。

  1. ユーザーにすべてを書き出すように強制するため、すべての変換は明示的です
  2. ユーザーが何をしているかを知っていると仮定し、任意の型を他の型に変換します
  3. 型セーフなジェネリック(テンプレート、新しい式)、明示的なユーザー定義の変換演算子を使用して完全な型システムを実装し、コンパイラが見えないものだけを暗黙的に安全にする明示的な変換を強制する

まあ、1はいものであり、何かを成し遂げるのに実際的な障害ですが、細心の注意が必要な場所で本当に使用されるかもしれません。Cは実装が簡単な2を大まかに選択し、3は実装が難しく安全な3のC ++を選択しました。


例外を後で追加し、テンプレートを後で追加して、3に到達するのは長く困難な道でした。
JDługosz

このような完全に安全な型システムには、テンプレートは本当に必要ありませ。Hindley-Milnerベースの言語は、暗黙の変換がまったくなく、型を明示的に記述する必要もなく、テンプレートなしでうまく機能します。(もちろん、これらの言語は、型消去/ガベージコレクション/より高次のポリモーフィズムに依存しています。C++はむしろ回避します。)
leftaroundabout

1

定義により、voidポインターは何でも指すことができます。どのポインターもvoidポインターに変換できるため、まったく同じ値に戻って変換することができます。ただし、他の型へのポインターには、位置合わせの制限などの制約がある場合があります。たとえば、文字は任意のメモリアドレスを占有できますが、整数は偶数アドレス境界で開始する必要があるアーキテクチャを想像してください。特定のアーキテクチャでは、整数ポインターは一度に16、32、または64ビットをカウントするため、char *が実際にint *の数値の倍数を持ち、メモリ内の同じ場所を指している場合があります。この場合、void *から変換すると、実際には回復できないビットが四捨五入されるため、元に戻せません。

簡単に言えば、ボイドポインターは、他のポインターが指すことができないものを含め、何でも指すことができます。したがって、voidポインターへの変換は安全ですが、その逆ではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.