1. ホーム
  2. c++

[解決済み] std::reference_wrapperとシンプルポインタの違い?

2022-08-24 21:23:33

質問

なぜ std::reference_wrapper ? どこで使うべき?単純なポインタとどう違うのですか?単純なポインタと比較して、その性能はどうですか?

どのように解決するのですか?

std::reference_wrapper は、テンプレートと組み合わせて使用すると便利です。これは、オブジェクトへのポインタを格納することでオブジェクトをラップし、通常のセマンティクスを模倣しながら再割り当てとコピーを可能にします。また、特定のライブラリテンプレートには、オブジェクトの代わりに参照を格納するように指示します。

ファンクタをコピーするSTLのアルゴリズムについて考えてみましょう。ファンクタ自身の代わりにファンクタを参照する参照ラッパーを渡すだけで、そのコピーを回避することができます。

unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state

これがうまくいくのは

  • ... reference_wrapper s オーバーロード operator() で、これらは参照する関数オブジェクトと同じように呼び出すことができます。

    std::ref(myEngine)() // Valid expression, modifies myEngines state
    
    
  • ... (通常の参照と異なり) コピー (および割り当て) は reference_wrappers はポインティを割り当てるだけです。

    int i, j;
    auto r = std::ref(i); // r refers to i
    r = std::ref(j); // Okay; r refers to j
    r = std::cref(j); // Error: Cannot bind reference_wrapper<int> to <const int>
    
    

参照ラッパーをコピーすることは、実質的にポインタをコピーすることと同じであり、それは限りなく安いものです。それを使うことに内在する全ての関数呼び出し(例えば operator() へのもの) は、ワンライナーであるため、単にインライン化されるべきです。

reference_wrapper std::refstd::cref :

int i;
auto r = std::ref(i); // r is of type std::reference_wrapper<int>
auto r2 = std::cref(i); // r is of type std::reference_wrapper<const int>

template引数には,参照されるオブジェクトの型とcv-qualificationを指定します. r2const int への参照のみを生成します。 const int . ラッパーを参照するための呼び出しは const ファンクタが含まれる参照ラッパーを呼び出すと const メンバー関数 operator() s.

Rvalueイニシャライザーは、許可することは良いことよりも害が大きいので、許可されません。rvalue はいずれにせよ移動されるので ( コピー消去を保証する は部分的に回避されます)、私たちはセマンティクスを改善しません。しかし、参照ラッパーは被指摘者の寿命を延長しないので、ぶら下がりポインタを導入することはできます。

ライブラリの相互作用

前述したように make_tuple に参照を保存するように指示することができます。 tuple に対応する引数を渡すことで reference_wrapper :

int i;
auto t1 = std::make_tuple(i); // Copies i. Type of t1 is tuple<int>
auto t2 = std::make_tuple(std::ref(i)); // Saves a reference to i.
                                        // Type of t2 is tuple<int&>

とは若干異なることに注意してください。 forward_as_tuple : ここでは、引数としてのrvalueは許可されていません。

std::bind は同じ挙動を示します。引数はコピーされませんが、もし引数が reference_wrapper . その引数(あるいはファンクタ!)がコピーされる必要がなく、スコープ内にとどまり、その間に bind -ファンクタが使われている間、スコープに残る場合に便利です。

通常のポインタとの違い

  • 構文的な間接指示の追加レベルはありません。ポインターは、それらが参照するオブジェクトへの lvalue を得るためにデリファレンスされなければなりません。 reference_wrapper には暗黙の 変換演算子 を持ち、ラップしているオブジェクトのように呼び出すことができます。

    int i;
    int& ref = std::ref(i); // Okay
    
    
  • reference_wrapper は、ポインタとは異なり、NULL状態を持ちません。初期化には 参照か別の reference_wrapper .

    std::reference_wrapper<int> r; // Invalid
    
    
  • 類似点としては、浅いコピーセマンティクスが挙げられます。ポインタと reference_wrapper は再割り当てすることができます。