19

TL;DR

次のコードがあるとします。

int* ptr;
*ptr = 0;

インダイレクションを適用する前にの左辺値から右辺値への変換が*ptr必要ですか?ptr

標準は、多くの場所で左辺値から右辺値へのトピックをカバーしていますが、 * 演算子がそのような変換を必要とするかどうかを判断するのに十分な情報を指定していないようです。

詳細

左辺値から右辺値への変換については、N3485のセクション左辺値から4.1 右辺値への変換パラグラフ1で説明されており、次のように述べられています ( employment mine going forward ):

非関数型、非配列型 T の glvalue (3.10) は prvalue に変換できます。glvalue が参照するオブジェクトが T 型のオブジェクトではなく、T から派生した型のオブジェクトでもない場合、またはオブジェクトが初期化されていない場合、この変換を必要とするプログラムの動作は未定義です .[...]

それで*ptr = 0; 、この変換が必要ですか?

4セクションパラグラフ1に行くと、次のように書かれています。

[...]必要に応じて、式を 必要な変換先の型に変換するために、標準の変換シーケンスが式に適用されます。

では、いつ必要なのですか?セクション5 Expressionsを見ると、左辺値から右辺値への変換はパラグラフ9で言及されています。

glvalue 式が、そのオペランドの prvalue を予期する演算子のオペランドとして現れるときはいつでも、左辺値から右辺値へ (4.1)、配列からポインターへ (4.2)、または関数からポインターへ (4.3) の標準変換が行われます。式を prvalue に変換するために適用されます。[...]

そしてパラグラフ11には次のように書かれています:

一部のコンテキストでは、式はその副作用のためにのみ表示されます。このような式は破棄値式と呼ばれます。[...] 左辺値から右辺値への変換 (4.1) は、式が volatile 修飾された型の左辺値であり、次のいずれかである場合にのみ適用されます [ ...]

どちらの段落もこのコード サンプルには適用されないようで、5.3.1 単項演算子の段落1には次のように書かれています。

単項 * 演算子は間接参照を実行します。それが適用される式は、オブジェクト型へのポインター、または関数型へのポインターであり、結果は、式が指すオブジェクトまたは関数を参照する左辺値になります。式の型が「T へのポインタ」の場合、結果の型は「T」になります。[ 注: 不完全な型 (cv void 以外) へのポインタによる間接化は有効です。このようにして得られた左辺値は、限られた方法で使用できます (たとえば、参照を初期化するため)。この左辺値は、prvalue に変換してはなりません。4.1 を参照してください。—終わりのメモ]

ポインターの値を必要としないようで、ポインターの変換の要件が見当たりません。何か不足していますか?

なぜ私たちは気にするのですか?

インダイレクションを適用する前に左辺値から右辺値への変換が必要なため、初期化されていないポインターの使用は未定義の動作であると主張する他の質問の回答とコメントを見てきました。ptr例: C++ 標準は、初期化されていないポインターの逆参照が未定義の動作であると正確にどこで言っていますか? はこの議論を行っており、標準の最近のドラフト バージョンのいずれにも示されている内容と議論を一致させることはできません。私はこれを数回見たので、明確にしたいと思いました。

上記のリンクされた質問で指摘したように、未定義の動作に到達する他の方法があるため、未定義の動作の実際の証明はそれほど重要ではありません。

4

2 に答える 2

13

いわば斜めの角度からアプローチしていると思います。§5.3.1/1 によると:

単項演算*子は間接参照を実行します。適用される式は、オブジェクト型へのポインター、または関数型へのポインターであり、結果は、式が指すオブジェクトまたは関数を参照する左辺値になります。式の型が「T へのポインター」の場合、結果の型は「T」になります。</p>

これは左辺値から右辺値への変換については触れていませんが、式がオブジェクトまたは関数へのポインターである必要があります。初期化されていないポインターは (おそらく偶然を除いて) そのようなものではないため、逆参照を試みると未定義の動作が発生します。

于 2014-01-10T20:25:14.067 に答える
4

私の質問に答えられないという不満足なものではありますが、この時点では答えのように見えるので、質問の更新セクションを答えに変換しました。

dypは、非常によく似た分野をカバーする 2 つの関連するスレッドを教えてくれました。

コンセンサスは、標準が正しく指定されていないため、私が探している答えを提供できないということです。Joseph Mansfield 、この仕様の欠如に関する欠陥レポートを投稿しまし。明らかになるかもしれません。

標準の意図に関して、いくつかの常識的な議論が必要です。論理的には、操作でそのオペランドの値を使用する必要がある場合、オペランドは prvalue であると主張できます。もう 1 つの議論は、 C99 ドラフト標準を振り返ると、左辺値から右辺値への変換がデフォルトで行われ、例外が記録されているということです。ドラフト C99 標準の関連セクションは、6.3.2.1 Lvalues、arrays、および function designatorsパラグラフ2であり、次のように述べられています。

sizeof 演算子、単項 & 演算子、++ 演算子、 -- 演算子、または . の左オペランドの場合を除きます。演算子または代入演算子の場合、配列型を持たない左辺値は、指定されたオブジェクトに格納されている値に変換されます (左辺値ではなくなります)。[…]

これは基本的に、いくつかの例外を除いて、オペランドが格納された値に変換されることを示しています。これは、間接参照は例外ではないため、これがC++でも同様であることが明確にされている場合、実際に私の質問に対する答えはyesになります。

左辺値から右辺値への変換が義務付けられているかどうかを明確にすることよりも、未定義の動作の証拠を明確にすることの方が重要ではありませんでした。未定義の動作を証明したい場合は、別のアプローチがあります。Jerry のアプローチは常識的なものであり、その間接化では、式がオブジェクトまたは関数へのポインターである必要があり、不確定な値は偶然に有効なオブジェクトを指すだけです。一般に、C99 ドラフト標準とは異なり、ドラフト C++ 標準は、不定値の使用が未定義であると明示的に宣言していません。例外はイテレータであり、拡張ポインターにより、特異値の概念がありますそして、セクション24.2.1で次のように説明されています。

[…][ 例: 初期化されていないポインター x (int* x; と同様) の宣言の後、x は常にポインターの特異値を持つと想定する必要があります。— 例の終了 ] […] 逆参照可能な値は、常に特異ではありません。

と:

無効なイテレータは、単数の可能性があるイテレータです。268

脚注 268 には次のように書かれています。

ポインターは反復子であるため、この定義はポインターに適用されます。無効化されたイテレータを逆参照した場合の影響は未定義です。

C++1yでは言語が変更され、一部の例外を除いて未定義の中間値を使用する明示的なステートメントがあります

于 2014-01-19T05:58:03.983 に答える