的std ::的reference_wrapper和简单的指针之间的区别是什么?的std ::的re

2019-05-13 05:07发布

为什么有必要拥有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::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>

模板参数指定的对象的类型和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>

  1. 这是给引用语义为值参数的函数模板传递的对象。 例如,你可以有你想传递一个大函数对象std::for_each()它通过值取它的功能对象参数。 为了避免复制的对象,你可以使用

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

    传递参数作为std::reference_wrapper<T>std::bind()表达是相当普遍的通过引用而不是通过值结合参数。

  2. 当使用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


文章来源: Difference between std::reference_wrapper and simple pointer?