Skip to content

effective_modern_c++ Item24_P158_1

 1template<typename T>
 2void f(T&& param); // param is a universal reference
 3
 4// lvalue passed to f; param's type is
 5// Widget& (i.e., an lvalue reference)
 6Widget w;
 7f(w); 
 8
 9// rvalue passed to f; param's type is
10// Widget&& (i.e., an rvalue reference)
11f(std::move(w));

这里涉及到 C++11 引入的右值引用和"完美转发"的概念,以及模板类型推导与引用折叠规则。

首先,当我们看到 T&& 的形式,并且 T 是一个模板参数,这通常被称为"通用引用"或"转发引用"。这种引用的类型推导会受到传递给它的实参的影响。

  1. 传递 lvalue:

当你有如下代码:

1Widget w;
2f(w);  // lvalue passed to f

在这种情况下,因为 w 是一个 lvalue,模板类型推导会使 T 成为 Widget&,这样 param 的类型(T&&)就变成了 Widget& &&。根据 C++ 的引用折叠规则,Widget& && 会折叠成 Widget&,所以 param 的类型是 lvalue 引用。

  1. 传递 rvalue:

当你有如下代码:

1f(std::move(w));  // rvalue passed to f

std::move(w) 返回一个右值引用,所以这里传递给 f 的是一个 rvalue。在这种情况下,模板类型推导会使 T 成为 Widget,因此 param 的类型(T&&)就是 Widget&&,即 rvalue 引用。

总结:

  • 对于 lvalue,T 被推导为 lvalue 引用类型,因此 T&& 成为 lvalue 引用。
  • 对于 rvalue,T 被推导为普通类型(非引用),因此 T&& 保持为 rvalue 引用。

这种模式允许函数模板 f 能够接受任何类型的参数,无论是 lvalue 还是 rvalue,并且通过 std::forward(与 std::move 类似,但它根据参数的类型来决定是否应该移动)实现完美转发。这是 C++11 及其后续版本中用于实现像 std::make_sharedstd::make_uniquestd::thread 之类的库功能的关键技术。


引用折叠是 C++11 引入的一个规则,主要用于处理模板和右值引用时可能出现的复合引用类型。规则如下:

  1. T& & 折叠为 T&
  2. T& && 折叠为 T&
  3. T&& & 折叠为 T&
  4. T&& && 折叠为 T&&

基本上,只要你有任何形式的 lvalue 引用 (&),结果总是一个 lvalue 引用。只有当两个引用都是 rvalue 引用 (&&) 时,结果才是 rvalue 引用。

为什么需要引用折叠规则呢?

主要是因为模板和类型推导,尤其是在使用通用引用(也称为转发引用)时。例如:

1template<typename T>
2void foo(T&& arg) {
3    // ...
4}

在这种情况下,T&& 是一个通用引用,它可以绑定到 lvalue 或 rvalue。当绑定到 lvalue 时,T 被推导为 Type&,因此 T&& 实际上变为 Type& &&,根据引用折叠规则,它折叠为 Type&

这种行为允许我们在模板中实现所谓的"完美转发",这意味着我们可以精确地将传递给函数的参数转发(或传递)给另一个函数,同时保持其原始的值类别(lvalue 或 rvalue)。

Related Posts

  1. effective_modern_c++ Item24_P156_1