std::reference_wrapper 和简单指针有什么区别?

新手上路,请多包涵

为什么需要 std::reference_wrapper ?应该在哪里使用?它与简单的指针有何不同?它的性能与简单指针相比如何?

原文由 Laurynas Lazauskas 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
2 个回答

std::reference_wrapper 与模板结合使用很有用。它通过存储指向对象的指针来包装对象,允许在模仿其通常语义的同时重新分配和复制。它还指示某些库模板存储引用而不是对象。

考虑 STL 中复制函子的算法:您可以通过简单地传递引用函子而不是函子本身的引用包装器来避免该复制:

 unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state

这有效,因为……

  • 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>

复制引用包装器实际上等同于复制指针,这很便宜。使用它时固有的所有函数调用(例如 operator() 的函数调用)都应该被内联,因为它们是单行的。

reference_wrapper 通过 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() s。

不允许使用右值初始化器,因为允许它们弊大于利。由于无论如何都会移动右值(并且 保证复制省略,即使部分避免),我们不改进语义;我们可以引入悬空指针,因为引用包装器不会延长指针的生命周期。

图书馆互动

如前所述,可以指示 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 函子时保持在范围内,则很有用。

与普通指针的区别

  • 没有额外的语法间接级别。必须取消引用指针才能获得指向它们所引用对象的左值; reference_wrapper s 有一个隐式 转换运算符,可以像它们包装的对象一样调用。
   int i;
  int& ref = std::ref(i); // Okay

   std::reference_wrapper<int> r; // Invalid

  • 一个相似之处是浅拷贝语义:指针和 reference_wrapper 可以重新分配。

原文由 Columbo 发布,翻译遵循 CC BY-SA 3.0 许可协议

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

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

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

  1. 当使用 std::reference_wrapper<T>std::make_tuple() 时,对应的元组元素变成 T& 而不是 T
    T object;
   f(std::make_tuple(1, std::ref(object)));

原文由 Dietmar Kühl 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题