なぜ 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 overload 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()
への呼び出し)は、1行であるため、単にインライン化する必要があります。
_reference_wrapper
_ sは _std::ref
_および_std::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>
_
テンプレート引数は、参照されるオブジェクトのタイプとcv-qualificationを指定します。 _r2
_は_const int
_を参照し、_const int
_への参照のみを生成します。 const
ファンクターを含むラッパーを参照する呼び出しは、const
メンバー関数operator()
sのみを呼び出します。
右辺値の初期化子は、許可するよりも害があるため、許可されません。とにかく右辺値が移動するので(および 保証されたコピーの省略 でも一部は回避されます)、セマンティクスを改善しません。ただし、参照ラッパーはポインティの有効期間を延長しないため、ダングリングポインターを導入できます。
前述のように、対応する引数を_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
_とわずかに異なることに注意してください。ここでは、引数としての右辺値は許可されていません。
_std::bind
_ は同じ動作を示します:引数をコピーしませんが、_reference_wrapper
_の場合は参照を保存します。その引数(またはファンクター!)をコピーする必要はないが、bind
- functorが使用されている間スコープ内に留まる場合に便利です。
追加のレベルの構文間接指定はありません。ポインターは、参照するオブジェクトへの左辺値を取得するために逆参照する必要があります。 _reference_wrapper
_ sには暗黙的な 変換演算子 があり、ラップするオブジェクトのように呼び出すことができます。
_int i;
int& ref = std::ref(i); // Okay
_
_reference_wrapper
_ sは、ポインターとは異なり、null状態を持ちません。これらは 参照または別の_reference_wrapper
_ で初期化する必要があります。
_std::reference_wrapper<int> r; // Invalid
_
類似点は、浅いコピーのセマンティクスです。ポインターと_reference_wrapper
_ sを再割り当てできます。
_std::reference_wrapper<T>
_には、少なくとも2つの動機付けの目的があります。
関数テンプレートに値パラメーターとして渡されるオブジェクトに参照セマンティクスを与えることです。たとえば、std::for_each()
に渡す大きな関数オブジェクトがあり、その関数オブジェクトの値を値で受け取ります。オブジェクトのコピーを避けるために、次を使用できます
_std::for_each(begin, end, std::ref(fun));
_
引数を_std::reference_wrapper<T>
_としてstd::bind()
式に渡すことは、値ではなく参照によって引数をバインドするために非常に一般的です。
std::make_Tuple()
で_std::reference_wrapper<T>
_を使用すると、対応するTuple要素はT
ではなく_T&
_になります。
_T object;
f(std::make_Tuple(1, std::ref(object)));
_
自己文書化コードに関する別の違いは、reference_wrapper
は、基本的にオブジェクトの所有権を否認します。対照的に、unique_ptr
は所有権を表明しますが、裸のポインタは所有される場合とされない場合があります(関連する多くのコードを見ずに知ることはできません)。
vector<int*> a; // the int values might or might not be owned
vector<unique_ptr<int>> b; // the int values are definitely owned
vector<reference_wrapper<int>> c; // the int values are definitely not owned
コンテナで使用できるように、参照の便利なラッパーと考えることができます。
_std::vector<std::reference_wrapper<T>> vec; // OK - does what you want
std::vector<T&> vec2; // Nope! Will not compile
_
基本的には_T&
_のCopyAssignable
バージョンです。参照が必要なときはいつでも、割り当て可能でなければならない場合は、_std::reference_wrapper<T>
_またはそのヘルパー関数std::ref()
を使用してください。または、ポインターを使用します。
その他の癖:sizeof
:
_sizeof(std::reference_wrapper<T>) == sizeof(T*) // so 8 on a 64-bit box
sizeof(T&) == sizeof(T) // so, e.g., sizeof(vector<int>&) == 24
_
そして比較:
_int i = 42;
assert(std::ref(i) == std::ref(i)); // ok
std::string s = "hello";
assert(std::ref(s) == std::ref(s)); // compile error
_