9
struct A {
    A(int) : i(new int(783)) {
        std::cout << "a ctor" << std::endl;
    }

    A(const A& other) : i(new int(*(other.i))) {
        std::cout << "a copy ctor" << std::endl;
    }

    ~A() {
        std::cout << "a dtor" << std::endl;
        delete i;
    }

    void get() {
        std::cout << *i << std::endl;
    }

private:
    int* i;
};

const A& foo() {
    return A(32);
}

const A& foo_2() {
    return 6;
}

int main()
{
    A a = foo();
    a.get();
}

ローカル値への参照を返すのは悪いことです。しかし一方で、const 参照は一時オブジェクトの有効期間を延長する必要があります。

このコードは UB 出力を生成します。だから延命はない。

なんで?つまり、誰かが何が起こっているのかを段階的に説明できますか?

私の推論チェーンのどこに誤りがありますか?

フー():

  1. A(32) - 担当者

  2. return A(32) - ローカル オブジェクトへの const 参照が作成され、返されます

  3. A a = foo(); - a は foo() 戻り値によって初期化され、戻り値はスコープ外 (式外) になり、破棄されますが、a は既に初期化されています。

(ただし、実際にはデストラクタはコピーコンストラクタの前に呼び出されます)

foo_2():

  1. return 6 - タイプ A の temp オブジェクトが暗黙的に作成され、このオブジェクトへの const 参照が作成され (寿命を延ばします)、返されます

  2. A a = foo(); - a は foo() 戻り値によって初期化され、戻り値はスコープ外 (式外) になり、破棄されますが、a は既に初期化されています。

(ただし、実際にはデストラクタはコピーコンストラクタの前に呼び出されます)

4

2 に答える 2

12

特定のコンテキストごとの一時的な存続期間延長のルールは、言語仕様で明示的に説明されています。そしてそれは言う

12.2 一時オブジェクト

5 2 番目のコンテキストは、参照がテンポラリにバインドされている場合です。[...] 関数 return ステートメント (6.6.3) の戻り値への一時的なバインドは、関数が終了するまで持続します。[...]

一時オブジェクトは、関数の終了時に破棄されます。これは、受信者オブジェクトの初期化が始まる前に発生します。

あなたの一時的なものは、どういうわけかそれより長く生きるべきだと思い込んでいるようです。どうやら、一時的なものは完全な式の終わりまで存続するというルールを適用しようとしているようです。ただし、その規則は、関数内で作成された一時オブジェクトには適用されません。そのような一時的存在の寿命は、独自の専用ルールによって管理されます。

誰かが返された参照を使用しようとすると、あなたfooとあなたの両方が未定義の動作を生成します。foo_2

于 2012-07-31T19:38:35.173 に答える
4

「関数が終了するまで」誤って干渉しています。本当にconst参照を使用してオブジェクトの寿命を超えたい場合はfoo

A foo() {
    return A(32);
}
int main() {
    const A& a = foo();
}

期待どおりに拡張したい場合は、from foo valueから戻り、const参照を使用して戻り値を参照する必要があります。

@AndreyTが言ったように、オブジェクトは。を持つ関数で破棄されますconst &。オブジェクトがを超えて存続することを望んでいるため、のreturnタイプのどこにも(または)を 含めるfooべきではありません。オブジェクトを存続させる必要がある関数であるため、最初に言及する必要があります。const &&foofooconst &main

戻り値でAのコピーが作成されているように見えるため、この値による戻り値のコードは遅いと思うかもしれませんが、これは正しくありません。ほとんどの場合、コンパイラーはAをその最終的な場所(つまり、呼び出し元の関数のスタック上)に1回だけ作成してから、関連する参照を設定できます。

于 2012-08-01T01:13:45.557 に答える