17

いくつかの質問に答えた後、私は今日この実験を構築しました

struct A { 
  bool &b; 
  A(bool &b):b(b) { } 
  ~A() { std::cout << b; }  
  bool yield() { return true; } 
}; 

bool b = A(b).yield();

int main() { }

b動的初期化によってfalse設定する前に(ゼロ初期化の結果として)値があります。終了trueの初期化の前に一時が破棄された場合は、を出力します。それ以外の場合はを出力します。bfalsetrue

仕様によると、一時は完全な式の最後に破棄されます。これは、の初期化では順序付けられていないようですb。だから私は疑問に思う

  • 仕様では、実装が異なる実行の両方falseで印刷することを許可していますか?true

Clangfalseは上記を印刷し、GCCはを印刷しtrueます。これは私を混乱させます。順序を定義する仕様テキストを見逃しましたか?

4

3 に答える 3

8

真、偽、または多少関係のない理由で、何も印刷できないと思います。

真または偽の部分は(あなたが言ったように)、一時Aオブジェクトの破棄はの動的初期化に関して順序付けられていないということですb

;bの作成/初期化に関して、の初期化が順序付けられていないため、まったく可能性はありません。std::cout一時的なものを破棄しようとすると、coutまだ作成/初期化されていない可能性があるため、何かを印刷しようとすると、その時点ではまったく機能しない可能性があります。[編集:これはC ++ 98/03に固有であり、C++11には適用されません。]

編集:これが私が少なくともシーケンスを見る方法です:

ここに画像の説明を入力してください

Edit2:§12.2/ 4を(もう一度)読み直した後、図をもう一度変更しました。§12.2/4は言う:

完全な表現の終わりとは異なる時点で一時的なものが破壊される2つのコンテキストがあります。最初のコンテキストは、オブジェクトを定義する宣言子の初期化子として式が表示される場合です。そのコンテキストでは、式の結果を保持する一時は、オブジェクトの初期化が完了するまで持続します。オブジェクトは、一時的なコピーから初期化されます。このコピー中に、実装はコピーコンストラクターを何度も呼び出すことができます。一時的なものは、コピーされた後、初期化が完了する前または完了すると破棄されます。

この式はオブジェクトを定義する宣言子の初期化子であると思います。そのためtrue、戻り値から直接ではなく、式の値(この場合は)のコピーからオブジェクトを初期化する必要があります。の場合true、これはおそらく違いのない区別ですが、現在の図は技術的に正確であると思います。

trueこれはまた、完全な表現の最後に一時的な保持を破棄する必要がないことをかなり明確にしているので(私は思う)、それを反映するために図を再描画しました。

このセクションはC++0x / C ++ 11でなくなったので、図を(もう一度)再描画して、2つの違い(およびこの部分がC ++ 11でどれだけ単純になったのか)を示しました。 。

于 2011-04-22T23:40:39.347 に答える
2

(C ++ 03標準の引用)

最初に§12.2/3があります:

実装が重要なコンストラクター(12.1)を持つクラスの一時オブジェクトを導入する場合、その一時オブジェクトに対してコンストラクターが呼び出されるようにする必要があります。同様に、デストラクタは、重要なデストラクタ(12.4)を使用して一時的に呼び出されるものとします。一時オブジェクトは、それらが作成されたポイントを(字句的に)含む完全式(1.9)を評価する最後のステップとして破棄されます。これは、その評価が例外のスローで終了した場合でも当てはまります。

§1.9/13のため、これは赤いニシンだと思います。

[注:C ++の特定のコンテキストでは、式(5.18)以外の構文構造から生じる完全な式の評価が発生します。たとえば、8.5では、初期化子の1つの構文は次のとおりです。

    ( expression-list )

ただし、結果の構成は、引数リストとしてexpression-listを使用するコンストラクター関数に対する関数呼び出しです。このような関数呼び出しは完全な式です。たとえば、8.5では、初期化子の別の構文は次のとおりです。

    = initializer-clause

ただし、結果の構成は、引数として1つの割り当て式を持つコンストラクター関数に対する関数呼び出しである可能性があります。この場合も、関数呼び出しは完全な式です。]

これA(b).yield()は、それ自体が完全な表現であり、ここでは§12.2/3を無関係にすることを意味します。

次に、シーケンスポイントに入ります-§1.9/ 7:

揮発性左辺値(3.10)で指定されたオブジェクトへのアクセス、オブジェクトの変更、ライブラリI / O関数の呼び出し、またはこれらの操作のいずれかを実行する関数の呼び出しはすべて副作用であり、実行環境の状態の変化です。式の評価は副作用を引き起こす可能性があります。シーケンスポイントと呼ばれる実行シーケンスの特定の指定されたポイントで、前の評価のすべての副作用が完了し、後続の評価の副作用が発生していないものとします。

§1.9/16:

各完全式の評価が完了すると、シーケンスポイントがあります。

および§1.9/17:

関数を呼び出す場合(関数がインラインであるかどうかに関係なく)、すべての関数の引数(存在する場合)の評価後、関数本体の式またはステートメントの実行前に実行されるシーケンスポイントがあります。戻り値のコピー後、関数外の式の実行前にもシーケンスポイントがあります。

すべてをまとめると、 Clangは正しく、GCC(およびMSVC 2010 SP1)は間違っていると思います-式の結果を保持する一時的なもの(§12.2/ 4に従って寿命が延長されています)は、boolから返されますA::yield()。呼び出されるA一時。yield§1.9を考慮に入れるA::yield()と、一時的なAものが破棄される呼び出しの後にシーケンスポイントが存在する必要があります。

于 2011-04-22T23:49:22.423 に答える
2

まず、以前ここにあった段落を明確にするために、ここでb独自の (動的) 初期化を使用することは UB ではありません。式が評価される前は、初期化されbていませんが、ゼロで初期化されています。


一時的なAものは、完全な式と同じくらい正確に存続する必要があります。

一時オブジェクトは、それらが作成されたポイントを (レキシカルに) 含む完全な式 (1.9) を評価する最後のステップとして破棄されます。

[ISO/IEC 14882:2003(E) 12.2/3]

bool b = A(b).yield();は宣言であり、ステートメントであり、式ではありません。手元の式は、 の RHS にのみ見つかり=ます。[ISO/IEC 14882:2003(E) A.6]

これは、動的な初期化が行われる前に一時的なものを破棄する必要があることを意味しますか? 確かに、値は初期化が完了するまで式1trueの結果を含むテンポラリに保持されますが、元のテンポラリは実際に変更される前に破棄する必要があります。Ab

したがってfalse、毎回出力が期待されます。


1

最初のコンテキストは、オブジェクトを定義する宣言子の初期化子として式が現れる場合です。そのコンテキストでは、式の結果を保持する一時的なものは、オブジェクトの初期化が完了するまで存続します。」

[ISO/IEC 14882:2003(E) 12.2/4]

于 2011-04-22T23:55:08.253 に答える