std :: reference_wrapper和简单指针之间的区别?
为什么需要有std::reference_wrapper
? 应该在哪里使用? 它与简单的指针有何不同? 它的性能如何比较简单的指针?
std::reference_wrapper
与模板结合使用很有用。 它通过存储一个指针来包装一个对象,允许在重新分配和复制的同时模仿其通常的语义。 它还指示某些库模板存储引用而不是对象。
考虑STL中复制仿函数的algorithm:您可以通过简单地传递引用仿函数而不是函数本身的引用包装来避免该复制:
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
-
…(联合国)像普通的引用,复制(和分配)
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>
模板参数指定所引用对象的types和cv-qualification; r2
指的是一个const int
,只会产生一个const int
的引用。 调用const
函数引用包装只会调用const
成员函数operator()
。
Rvalue初始值设定项目是不允许的,因为允许它们做弊大于利。 由于无论如何rvalues将被移动(并且保证副本的安全性,即使这部分地被避免),我们也不会改进语义。 我们可以引入悬挂指针,因为引用包装不会延长指针的生命周期。
图书馆互动
如前所述,可以指示make_tuple
通过将parameter passing给reference_wrapper来在结果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
略有不同:在这里,rvalues作为参数是不允许的。
std::bind
显示相同的行为:如果它是一个reference_wrapper
它不会复制参数,而是存储一个reference_wrapper
。 如果该参数(或函子!)不需要复制,但在使用bind
-functor时保持在作用域内,则可以使用。
与普通指针不同
-
没有额外的语法间接级别。 指针必须被解引用来获得它所指的对象的左值。
reference_wrapper
有一个隐式的转换运算符 ,可以像它们包装的对象一样被调用。int i; int& ref = std::ref(i); // Okay
-
与指针不同,
reference_wrapper
不具有空状态。 它们必须使用引用或其他reference_wrapper
进行初始化。std::reference_wrapper<int> r; // Invalid
-
相似之处是浅拷贝语义:指针和
reference_wrapper
可以被重新分配。
std::reference_wrapper<T>
至less有两个激励目的:
-
将参数语义赋予作为值parameter passing给函数模板的对象。 例如,你可能有一个大的函数对象,你想传递给
std::for_each()
,它的function对象参数的值。 为避免复制对象,可以使用std::for_each(begin, end, std::ref(fun));
将参数作为
std::reference_wrapper<T>
传递给std::bind()
expression式是通过引用绑定参数而不是通过值绑定的。 -
当使用
std::reference_wrapper<T>
和std::make_tuple()
,相应的元组元素变成了T&
而不是T
:T object; f(std::make_tuple(1, std::ref(object)));
你可以把它看作引用的一个便利的包装,以便你可以在容器中使用它们。
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()
。 或者使用一个指针。
由于它增加了一个额外的间接层,它将严重地比T&
更差(但可能可以忽略),在两者都可以使用的情况下,所以你只能在真的必须使用它时才使用它。
其他怪癖:
sizeof(std::reference_wrapper<T>) == sizeof(T*) // so 8 on a 64-bit box sizeof(T&) == sizeof(T) // so, eg, sizeof(vector<int>&) == 24
就自我logging代码而言,另一个区别在于,使用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