为什么有必要拥有std::reference_wrapper
? 应该在哪里应用? 它是如何从一个简单的指针有什么不同? 它的性能如何比较简单的指针?
Answer 1:
std::reference_wrapper
与模板组合使用。 它通过存储指向它的指针,从而允许重新分配和复制的同时模仿其通常语义包装的对象。 它也指示某些库模板存储引用,而不是对象。
考虑其复制函子的STL的算法:您可以通过传递一个参考包装指的是仿函数,而不是仿函数本身避免副本:
unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state
这工作,因为...
...
reference_wrapper
小号重载operator()
这样他们就可以被称为就像他们指的是函数对象:std::ref(myEngine)() // Valid expression, modifies myEngines state
...(UN)像普通的引用,复制(和分配)
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
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-资格称为; r2
是指const int
,并且只产生一个参考const int
。 拨打引用包装与const
在其中仿函数只能调用const
成员函数operator()
秒。
右值初始化是不允许的,因为允许他们会做弊大于利。 由于右值将被无论如何移动(与保证复制省略甚至避免真实部分),我们不提高语义; 我们虽然可以引入悬摆指针,作为参考包装不延长指针对象的生命周期。
图书馆互动
如前所述,一个可以指示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
■找一个隐式转换运算符 ,并且可以被称为如他们包装的对象。int i; int& ref = std::ref(i); // Okay
reference_wrapper
S,不像指针,没有空状态。 他们必须与被初始化或者引用或其它reference_wrapper
。std::reference_wrapper<int> r; // Invalid
相似性是浅拷贝语义:指针和
reference_wrapper
s时,可以重新分配。
Answer 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)));
Answer 3:
另一个不同之处,在自记录代码而言,是使用reference_wrapper
基本上disavows对象的所有权。 相比之下, 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
Answer 4:
你可以把它看作是围绕引用一个便利的包装,这样就可以在容器中使用它们。
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