16

通常、右辺値は const 参照 ( ) にバインドできconst SomeType&ます。それは言語に組み込まれています。ただし、std::reference_wrapper<const T>対応するオーバーロードが意図的に削除されるため、右辺値をコンストラクター引数として受け入れません。この矛盾の理由は何ですか?std::reference_wrapper値で渡す必要があるが参照セマンティクスを保持したい場合の参照変数の代替として「宣伝」されています。

言い換えれば、const &バインディングへの右辺値が安全であると見なされる場合、それは言語に組み込まれているため、なぜ C++11 の設計者は右辺値を でラップすることを許可しなかったのstd::reference_wrapper<const T>でしょうか?

これがいつ便利になるか、あなたは尋ねるかもしれません。例えば:

class MyType{};

class Foo { 
public:
    Foo(const MyType& param){} 
};

class MultiFoo {
public:
    MultiFoo(std::initializer_list<std::reference_wrapper<const MyType>> params){} 
};

int main()
{
    Foo foo{MyType{}}; //ok
    MultiFoo multiFoo{MyType{}, MyType{}}; //error
}
4

4 に答える 4

13

前書き

通常T const&、それに直接バインドされた一時オブジェクトの有効期間を延長できますが、参照がコンストラクタの背後に「隠れている」T&&場合、これは適用されません。

std::reference_wrapperは (意図的に) コピー可能であるため、一時オブジェクトが作成されたスコープをハンドルがエスケープするような方法で が使用された場合、参照されるオブジェクトへのハンドルは一時オブジェクトよりも長く存続する可能性がありますstd::reference_wrapper

これにより、ハンドルと参照される オブジェクト(つまり、一時オブジェクト) の間で有効期間の不一致が発生します。


LET'S PLAY " MAKE BELIEVE "

以下の違法なスニペットがあると想像してください。ここでstd::reference_wrapper、一時を受け入れるコンストラクターがあるふりをします。

また、コンストラクタに渡されたテンポラリの有効期間が延長されるふりをしましょう (実際にはそうではありませんが、実際には の直後に「死んで」います(1))。


typedef std::reference_wrapper<std::string const> string_ref;

string_ref get_ref () {
  string_ref temp_ref { std::string { "temporary" } }; // (1)

  return temp_ref; 
}

int main () {
  string_ref val = get_ref ();

  val.get (); // the temporary has been deconstructed, this is dangling reference!
}

一時は自動ストレージ期間で作成されるため、内部のスコープにバインドされたストレージに割り当てられますget_ref

get_ref後で戻ると、一時的なものは破棄されます。これは、元のオブジェクトが存在しないため、 in が無効なオブジェクトを参照することを意味しvalますmain

std::reference_wrapper上記が、のコンストラクターに一時値を受け入れるオーバーロードがない理由です。


もう一つの例

struct A {
  A (std::string const& r)
    : ref (r)
  { }

  std::string const& ref;
};

A foo { std::string { "temporary " } };

foo.ref = ...; // DANGLING REFERENCE!

std::string { "temporary" }標準で読み取れるように、の寿命は延長されません。

12.2p5 一時オブジェクト [class.temporary]

参照がバインドされている一時オブジェクト、または参照がバインドされているサブオブジェクトの完全なオブジェクトである一時オブジェクトは、次の例外を除き、参照の存続期間中持続します。

  • コンストラクターの ctor-initializer (12.6.2) の参照メンバーへの一時的なバインドは、コンストラクターが終了するまで持続します。

  • 関数呼び出し (5.2.2) の参照パラメーターへの一時的なバインドは、呼び出しを含む完全な式が完了するまで持続します。

  • 関数 return ステートメント (6.6.3) の戻り値に一時的にバインドされているものの有効期間は延長されません。一時的なものは、return ステートメントの完全な式の最後で破棄されます。

    • new-initializer (5.3.4)の参照への一時的なバインドは、 new-initializer を含む完全な式が完了するまで持続します

: コンストラクターは、オブジェクトの構築時に呼び出される「凝った」関数に過ぎないことに注意することが重要です。

于 2014-05-31T18:24:38.930 に答える
10

一時オブジェクトを参照に直接バインドすると、その寿命が長くなります。

struct Foo {};
Foo f() { return {}; }

void g() {
    f(); // temporary destroyed at end of full-expression
    const Foo& r = f(); // temporary destroyed at end of scope
    // r can still be used here ...
    // ...
    // all the way down to here
}

ただし、テンポラリに直接バインドする必要があります。次の例を検討してください。

struct Bar {
    Bar(const Foo& f): r(f) {}
    const Foo& r;
};

void h() {
    Bar b(f());
    // binding occurs through parameter "f" rather than directly to temporary "f()"
    // b.r is now a dangling reference! lifetime not extended
}

たとえできたとしても、一時的にラップすることはまったく役に立たないでしょう。

: 実際の参照ラッパー オブジェクトはポインターを使用するため、再割り当てが可能です。

struct Baz {
    Baz(const Foo& f): p(std::addressof(f)) {}
    Baz& operator=(const Foo& f) { p = std::addressof(f); return *this; }
    const Foo* p;
};

同じことが当てはまります。コンストラクターまたは代入演算子に渡された一時的なものは、呼び出しを含む完全な式の最後で破棄されます。

于 2014-05-31T18:26:29.057 に答える
1

変数はconst T&&移動も変更もできないため、使用する理由はありません ( a に対する利点や違いはありませんconst T&)。さらに、対応するテンポラリが生きていない場合、その参照を事後的に使用すると危険な場合があります (実際には危険ですそのような場合、未定義の動作が呼び出されるためです)。

結局のところ、右辺値参照コンストラクターを削除することは悪い考えではありません。

于 2014-05-31T19:10:31.430 に答える
1

const 参照をコピーすると、参照先のオブジェクトが存続するとは限らないため、コピーしないでください。次のコードは問題を示しています。

#include <iostream>

struct X { int x; };

struct Foo {
    const X& a;
    Foo(const X& na) : a(na) {} // here na is still OK
};

int main()
{
    Foo foo{X{3}}; // the temporary exists only for this expression
    // now the temporary is no longer alive and foo.a is a dangling reference
    std::cout << foo.a.x << std::endl; // undefined behavior
}

const 参照は、オブジェクト自体ではなく、X{3}のコンストラクターに対して一時的に有効にします。Fooぶら下がり参照を取得します。

この問題から保護するために、 と を使用した一時オブジェクトの使用は無効std::reference_wrapperstd::refなっています。

于 2014-05-31T18:38:13.220 に答える