32

ifStandardは、条件式の評価中に構築された一時オブジェクトの存続期間をどのように定義しますか?

私はこの情報を探して、10ページの$ 1.9の[10]を指す例で似たようなものを見つけました(ここでは新しい仕様の最終ドラフトを参照しています)。 Visual C ++の動作は、その例の理解とは異なるため、質問することにしました。

仕様への適切な参照を提供してください。


オブジェクトに名前を付けると、オブジェクトは全体にわたって持続します(つまり、ブロックとブロックifの両方ですが、終了する前に破棄されます)。truefalseif

例えば:

if ( MyClass x = f() ) { /* ... */ } else { /* ... */ }
nextInstruction();

x両方のブロックで使用できますが、呼び出されるif前に破棄されます。nextInstruction

しかし、名前を付けないとどうなりますか?

if ( f() ) { /* ... */ } else { /* ... */ }
nextInstruction();

仕様の参照部分についての私の理解では、によって返される値は、実行がブロックの1つ(forまたはfor )f()に入る前に破棄されます。truefalse

ただし、Visual C ++は、その一時オブジェクトを名前が付けられているかのように破棄します。編集: Tino Didriksenが指摘したように、ここではVisual C ++がうまく機能します。実際、今でもそれを確認しています。最初のテスト結果を見るときに間違いを犯したに違いありません!)


これは、一部のエッジケースでは重要です(ここでは、それらがどの程度発生する可能性があるか、またはそのようにコードを記述することが適切かどうかについては説明しません...)。

たとえば、次のようにします。

class ScopedLock {
public:
  ~ScopedLock() { if ( isLocked() ) unlock(); }

  operator bool() const { return isLocked(); }

  /* ... */

};

次のようなコードがある場合:

if ( ScopedLock lock = resource.lock() ) { /* ... */ }

実行がtrueブロックに入るとき、私たちはリソースを所有し、そのブロックを離れる前にリソースのロックが解除されないことを確認できます。

しかし、誰かが次のように書いた場合はどうなりますか?

if ( resource.lock() ) { /* ... */ }

ここで、一時的なデストラクタScopedLockが呼び出されることが重要です。これは、このコードが正しいかどうかを判断するためです(リソース使用量の意味で)。(ここでも、そのようなコードを書くことが一般的に悪いかどうかについての議論をスキップしましょう。それはこの質問のポイントではありません...)

4

3 に答える 3

12

ただし、Visual C ++は、その一時オブジェクトを名前が付けられているかのように破棄します。

いいえ、そうではありません...

与えられたコード

#include <iostream>

struct S {
    S() { std::cout << "S()" << std::endl; }
    S(const S&) { std::cout << "S(const S&)" << std::endl; }
    ~S() { std::cout << "~S()" << std::endl; }
    operator bool() const { return true; }
};

int main() {
    std::cout << "main 1" << std::endl;

    if (S s = S()) {
        std::cout << "if 1" << std::endl;
    }
    else {
        std::cout << "else 1" << std::endl;
    }

    std::cout << "main 2" << std::endl;

    if (S()) {
        std::cout << "if 2" << std::endl;
    }
    else {
        std::cout << "else 2" << std::endl;
    }

    std::cout << "main 3" << std::endl;
}

GNU g++4.5.1とg++4.7.0、VC++2010とVC++2012の出力は、まったく同じです。

main 1
S()
if 1
~S()
main 2
S()
~S()
if 2
main 3

ここで、名前付き一時はif / elseの後に破棄され、名前なし一時は条件の終了時に破棄されます。

于 2012-11-30T12:22:57.900 に答える
7

私の知る限り、VisualC++はこの点で間違っています。

実際、一時的なものは、それが作成された完全な式の最後で(ほとんどの場合)破棄されます。

§12.2/3:[...]一時オブジェクトは、それらが作成されたポイントを(字句的に)含む完全な式を評価する最後のステップとして破棄されます

選択ステートメント(ifおよびswitch)の定義を見ると、条件が式であることがわかります。

§6.4:
selection-statement:
  if ( condition ) statement
  if ( condition) statement else statement
  switch ( condition ) statement

condition:
  expression
  type-specifier-seq declarator = assignment-expression

したがって、条件で作成された一時的なものは、次のステートメントを実行する前に破棄する必要があります(constへの参照にバインドされている場合を除く)。

条件に新しい名前を導入するときの動作は、§6.4/3で指定されています。

条件[...]の宣言によって導入された名前は、その宣言の時点から、条件によって制御されるサブステートメントの終わりまでの範囲内にあります。

したがって、あなたの例でxは、はの2つのブランチのスコープ内にあり、ifを評価する前に破棄されnextInstruction()ます。

于 2012-11-30T11:43:31.823 に答える
3

これは、145ページの「C++の設計と進化」で見つけたものです。

void h(String s1, String s2)
{
    const char* p;
    if (p = s1+s2) {
        // ...
    }
}

保持しているオブジェクトの破棄はs1+s2、条件の最後に発生しますか、ifそれともステートメント全体の最後に発生しますか?答えはs1+s2、条件の終了時に保持しているオブジェクトが破棄されるということです。

于 2012-11-30T12:02:47.613 に答える