C++ std::reference_wrapper 和简单指针的区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26766939/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Difference between std::reference_wrapper and simple pointer?
提问by Laurynas Lazauskas
Why is there a need to have std::reference_wrapper
? Where should it be used? How is it different from a simple pointer? How its performance compares to a simple pointer?
为什么需要拥有std::reference_wrapper
?应该在哪里使用?它与简单的指针有何不同?它的性能与简单的指针相比如何?
采纳答案by Columbo
std::reference_wrapper
is useful in combination with templates. It wraps an object by storing a pointer to it, allowing for reassignment and copy while mimicking its usual semantics. It also instructs certain library templates to store references instead of objects.
std::reference_wrapper
与模板结合使用时很有用。它通过存储指向对象的指针来包装对象,允许重新分配和复制,同时模仿其通常的语义。它还指示某些库模板存储引用而不是对象。
Consider the algorithms in the STL which copy functors: You can avoid that copy by simply passing a reference wrapper referring to the functor instead of the functor itself:
考虑 STL 中复制函子的算法:您可以通过简单地传递引用函子而不是函子本身的引用包装来避免这种复制:
unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state
This works because…
这是有效的,因为…
…
reference_wrapper
s overloadoperator()
so they can be called just like the function objects they refer to:std::ref(myEngine)() // Valid expression, modifies myEngines state
…(un)like ordinary references, copying (and assigning)
reference_wrappers
just assigns the pointee.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>
...
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>
Copying a reference wrapper is practically equivalent to copying a pointer, which is as cheap as it gets. All the function calls inherent in using it (e.g. the ones to operator()
) should be just inlined as they are one-liners.
复制一个引用包装器实际上等同于复制一个指针,它尽可能便宜。使用它固有的所有函数调用(例如那些 to operator()
)应该只是内联,因为它们是单行的。
reference_wrapper
s are created via std::ref
and std::cref
:
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>
The template argument specifies the type and cv-qualification of the object referred to; r2
refers to a const int
and will only yield a reference to const int
. Calls to reference wrappers with const
functors in them will only call const
member function operator()
s.
模板参数指定所引用对象的类型和 cv 限定;r2
指的是 aconst int
并且只会产生对 的引用const int
。调用包含const
函子的引用包装器只会调用const
成员函数operator()
s。
Rvalue initializers are disallowed, as permitting them would do more harm than good. Since rvalues would be moved anyway (and with guaranteed copy elisioneven that's avoided partly), we don't improve the semantics; we can introduce dangling pointers though, as a reference wrapper does not extend the pointee's lifetime.
不允许使用右值初始值设定项,因为允许它们弊大于利。由于无论如何都会移动右值(并且即使部分避免也有保证的复制省略),我们不会改进语义;不过,我们可以引入悬空指针,因为引用包装器不会延长指针的生命周期。
Library interaction
图书馆互动
As mentioned before, one can instruct make_tuple
to store a reference in the resulting tuple
by passing the corresponding argument through a reference_wrapper
:
如前所述,可以通过将相应的参数传递make_tuple
给 a来指示在结果中存储引用: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&>
Note that this slightly differs from forward_as_tuple
: Here, rvalues as arguments are not allowed.
请注意,这与forward_as_tuple
: 此处略有不同:这里不允许将右值作为参数。
std::bind
shows the same behavior: It won't copy the argument but store a reference if it is a reference_wrapper
. Useful if that argument (or the functor!) need not be copied but stays in scope while the bind
-functor is used.
std::bind
显示相同的行为:它不会复制参数,但如果它是reference_wrapper
. 如果该参数(或函子!)不需要复制但在bind
使用 -函子时保持在范围内,则很有用。
Difference from ordinary pointers
与普通指针的区别
There is no additional level of syntactical indirection. Pointers have to be dereferenced to obtain an lvalue to the object they refer to;
reference_wrapper
s have an implicit conversion operatorand can be called like the object they wrap.int i; int& ref = std::ref(i); // Okay
reference_wrapper
s, unlike pointers, don't have a null state. They have to be initialized with either a reference or anotherreference_wrapper
.std::reference_wrapper<int> r; // Invalid
A similarity are the shallow copy semantics: Pointers and
reference_wrapper
s can be reassigned.
没有额外的语法间接级别。指针必须被取消引用以获得指向它们所引用对象的左值;
reference_wrapper
s 有一个隐式转换运算符,可以像它们包装的对象一样调用。int i; int& ref = std::ref(i); // Okay
reference_wrapper
s,与指针不同,没有空状态。它们必须用引用或另一个reference_wrapper
.std::reference_wrapper<int> r; // Invalid
一个相似之处是浅拷贝语义:指针和
reference_wrapper
s 可以重新分配。
回答by Dietmar Kühl
There are, at least, two motivating purposes of std::reference_wrapper<T>
:
至少有两个激励目的std::reference_wrapper<T>
:
It is to give reference semantics to objects passed as value parameter to function templates. For example, you may have a large function object you want to pass to
std::for_each()
which takes its function object parameter by value. To avoid copying the object, you can usestd::for_each(begin, end, std::ref(fun));
Passing arguments as
std::reference_wrapper<T>
to anstd::bind()
expression is quite common to bind arguments by reference rather than by value.When using an
std::reference_wrapper<T>
withstd::make_tuple()
the corresponding tuple element becomes aT&
rather than aT
:T object; f(std::make_tuple(1, std::ref(object)));
它是为作为值参数传递给函数模板的对象提供引用语义。例如,您可能有一个大型函数对象要传递给
std::for_each()
它,它按值获取其函数对象参数。为避免复制对象,您可以使用std::for_each(begin, end, std::ref(fun));
通过引用而不是值来绑定参数,将参数传递
std::reference_wrapper<T>
给std::bind()
表达式是很常见的。当将 an
std::reference_wrapper<T>
与std::make_tuple()
相应的元组元素一起使用时,它会变成 aT&
而不是 aT
:T object; f(std::make_tuple(1, std::ref(object)));
回答by Edward Loper
Another difference, in terms of self-documenting code, is that using a reference_wrapper
essentially disavows ownership of the object. In contrast, a unique_ptr
asserts ownership, while a bare pointer might or might not be owned (it's not possible to know without looking at lots of related code):
就自记录代码而言,另一个区别是使用 areference_wrapper
本质上否认对象的所有权。相比之下,aunique_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
回答by Barry
You can think of it as a convenience wrapper around references so that you can use them in containers.
您可以将其视为引用的便利包装器,以便您可以在容器中使用它们。
std::vector<std::reference_wrapper<T>> vec; // OK - does what you want
std::vector<T&> vec2; // Nope! Will not compile
It's basically a CopyAssignable
version of T&
. Any time you want a reference, but it has to be assignable, use std::reference_wrapper<T>
or its helper function std::ref()
. Or use a pointer.
这基本上是一个CopyAssignable
版本T&
。任何时候你想要一个引用,但它必须是可分配的,使用std::reference_wrapper<T>
或其辅助函数std::ref()
。或者使用指针。
Other quirks: sizeof
:
其他怪癖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
And comparison:
和比较:
int i = 42;
assert(std::ref(i) == std::ref(i)); // ok
std::string s = "hello";
assert(std::ref(s) == std::ref(s)); // compile error