web-dev-qa-db-ja.com

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

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

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

cでは機能しますが、C++では機能しません。 (注:C++ではmallocを使用すべきではないこと、またはnewを使用すべきではないことを知っています。代わりに、スマートポインタやSTLを優先する必要があります。これは純粋に好奇心から求められます。 )C標準では許可されているのに、C++標準ではこの暗黙のキャストが許可されないのはなぜですか?

30
wolfPack88

暗黙的な型変換は通常安全ではなく、C++はCよりも型付けに対してより安全なスタンスをとるためです。

Cは、変換がエラーである可能性が最も高い場合でも、通常は暗黙的な変換を許可します。これは、Cがプログラマーが何をしているかを正確に知っているとCが想定しているためであり、そうでない場合は、コンパイラーの問題ではなく、プログラマーの問題です。

C++は通常、エラーになる可能性のあるものを許可せず、型キャストで意図を明示的に示す必要があります。これは、C++がプログラマーに優しくしようとしているためです。

実際にさらに入力する必要があるのに、どうしてフレンドリーなのかと尋ねる場合があります。

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

その上、明示的なキャストを入力する必要があるという不便さは、警告されたはずのない、誤った割り当てによって引き起こされたバグを見つけるために何時間ものトラブルシューティングをするときの何時間もの不便さと比較して取るに足らないものです。

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

39
Mike Nakis

これが Stroustrupによる です。

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

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

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

最後に、彼は注意します:

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

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

C++では、タイプセーフな新しい演算子を使用します。

int * p = new int;

C++の設計と進化 でこれについて詳しく説明しています。

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

28
Gort the Robot

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

それは常に宣伝されます、はい、しかしほとんど安全に

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


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

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

まあ、1は醜く、何かを成し遂げるための実用的な障害ですが、細心の注意が必要な場合は本当に使用される可能性があります。 Cはおおむね2を選択し、実装が簡単で、C++は3を実装しています。

11
Useless

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

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

1
roserez