なぜ必要があるのstd::reference_wrapper
ですか?どこで使用すべきですか?単純なポインタとどう違うのですか?そのパフォーマンスは単純なポインタとどのように比較しますか?
.
しても、提案したとおりには機能しません(ある時点でオペレータードットの提案が採用および統合されない限り:))
get()
メンバー関数で使用するか、基になる型に暗黙的に変換して使用します。
なぜ必要があるのstd::reference_wrapper
ですか?どこで使用すべきですか?単純なポインタとどう違うのですか?そのパフォーマンスは単純なポインタとどのように比較しますか?
.
しても、提案したとおりには機能しません(ある時点でオペレータードットの提案が採用および統合されない限り:))
get()
メンバー関数で使用するか、基になる型に暗黙的に変換して使用します。
回答:
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::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
■ポインタとは異なり、null状態はありません。参照または別のいずれかreference_wrapper
で初期化する必要があります。
std::reference_wrapper<int> r; // Invalid
類似点は、浅いコピーのセマンティクスです。ポインターとをreference_wrapper
再割り当てできます。
std::make_tuple(std::ref(i));
よりも優れてstd::make_tuple(&i);
何らかの方法で?
i
は、への参照ではなくへのポインタを保存します。
少なくとも、以下の2つの動機付けの目的がありstd::reference_wrapper<T>
ます。
関数テンプレートに値パラメーターとして渡されるオブジェクトに参照セマンティクスを与えることです。たとえばstd::for_each()
、関数オブジェクトのパラメーターを値で受け取る、渡したい大きな関数オブジェクトがあるとします。オブジェクトのコピーを回避するには、次を使用できます
std::for_each(begin, end, std::ref(fun));
引数を渡すstd::reference_wrapper<T>
にstd::bind()
表現すると、参照することによってではなく、値で引数をバインドするのは非常に一般的です。
使用する場合std::reference_wrapper<T>
とstd::make_tuple()
対応するタプル要素になるT&
のではなくT
:
T object;
f(std::make_tuple(1, std::ref(object)));
fun
関数オブジェクト(つまり、関数呼び出し演算子を持つクラスのオブジェクト)であり、関数ではありません。fun
たまたま実際の関数であるstd::ref(fun)
場合、目的がなく、コードが遅くなる可能性があります。
自己文書化コードに関するもう1つの違い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
reference_wrapper
未所有であることが明らかであるだけでなく、それができないことnullptr
(shenanigansなし)のためにrawポインターよりも優れているため、ユーザーはnullptr
(shenanigansなしで)パスできないことを知っており、あなたがする必要がないことを知っています確認してください。
これは、参照をコンテナで使用できるようにするための便利なラッパーと考えることができます。
std::vector<std::reference_wrapper<T>> vec; // OK - does what you want
std::vector<T&> vec2; // Nope! Will not compile
基本的にはのCopyAssignable
バージョンですT&
。参照が必要なときはいつでも、割り当て可能、使用、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
reference_wrapper
コードをインライン化し、ポインタまたは参照を使用するコードと同じになることを期待します。
std::reference_wrapper
オブジェクトがnullにならないことが保証されています。クラスのメンバーについて考えてみましょうstd::vector<T *>
。すべてのクラスコードを調べて、このオブジェクトがをnullptr
ベクターに格納できるかどうかを確認するstd::reference_wrapper<T>
必要がありますが、では、有効なオブジェクトがあることが保証されています。
.
、代わりに使用するポインターです->