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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:42:09  来源:igfitidea点击:

Difference between std::reference_wrapper and simple pointer?

c++pointersc++11referencereference-wrapper

提问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_wrapperis 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_wrappers overload operator()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_wrappersjust 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_wrappers重载,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_wrappers are created via std::refand std::cref:

reference_wrappers 是通过std::refstd::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; r2refers to a const intand will only yield a reference to const int. Calls to reference wrappers with constfunctors in them will only call constmember 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_tupleto store a reference in the resulting tupleby passing the corresponding argument through a reference_wrapper:

如前所述,可以通过将相应的参数传递make_tuple给 a来指示在结果中存储引用:tuplereference_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::bindshows 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_wrappers have an implicit conversion operatorand can be called like the object they wrap.

    int i;
    int& ref = std::ref(i); // Okay
    
  • reference_wrappers, unlike pointers, don't have a null state. They have to be initialized with either a reference or another reference_wrapper.

    std::reference_wrapper<int> r; // Invalid
    
  • A similarity are the shallow copy semantics: Pointers and reference_wrappers can be reassigned.

  • 没有额外的语法间接级别。指针必须被取消引用以获得指向它们所引用对象的左值;reference_wrappers 有一个隐式转换运算符,可以像它们包装的对象一样调用。

    int i;
    int& ref = std::ref(i); // Okay
    
  • reference_wrappers,与指针不同,没有空状态。它们必须用引用或另一个reference_wrapper.

    std::reference_wrapper<int> r; // Invalid
    
  • 一个相似之处是浅拷贝语义:指针和reference_wrappers 可以重新分配。

回答by Dietmar Kühl

There are, at least, two motivating purposes of std::reference_wrapper<T>:

至少有两个激励目的std::reference_wrapper<T>

  1. 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 use

    std::for_each(begin, end, std::ref(fun));
    

    Passing arguments as std::reference_wrapper<T>to an std::bind()expression is quite common to bind arguments by reference rather than by value.

  2. When using an std::reference_wrapper<T>with std::make_tuple()the corresponding tuple element becomes a T&rather than a T:

    T object;
    f(std::make_tuple(1, std::ref(object)));
    
  1. 它是为作为值参数传递给函数模板的对象提供引用语义。例如,您可能有一个大型函数对象要传递给std::for_each()它,它按值获取其函数对象参数。为避免复制对象,您可以使用

    std::for_each(begin, end, std::ref(fun));
    

    通过引用而不是值来绑定参数,将参数传递std::reference_wrapper<T>std::bind()表达式是很常见的。

  2. 当将 anstd::reference_wrapper<T>std::make_tuple()相应的元组元素一起使用时,它会变成 aT&而不是 a T

    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_wrapperessentially disavows ownership of the object. In contrast, a unique_ptrasserts 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 CopyAssignableversion 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