通用引用的 standard/official 名称是什么?

What's the standard/official name for universal references?

我知道如果某个变量或参数被声明为 T&& 某些推导类型 T,则该变量或参数被广泛称为 通用引用 .

术语通用参考 是由 Scott Meyers 在他最初的演讲 “Universal References in C++11” 中引入的。但是,我想知道 通用引用 的 official/standard 术语是什么。

概览

众所周知,自 C++11 起,T&& 类型的参数称为 rvalue 引用 [ISO/IEC 14882:2011 §8.3.2/p2 参考资料 [dcl.ref] ]。也就是说,除非 T 是模板参数类型或 auto 或某个左值引用类型的 typedef

examples:

template<typename T>
void foo(T&& p) { // -> T is a template parameter
  ...
}

auto &&p = expression;

尽管从技术上讲,上述示例中的 T&& 仍然是右值引用,但其行为与常规引用有很大不同。

自然会问 "why this special case doesn't have a special syntax"。答案是 && 语法是 C++ 委员会故意为这个特殊结构重载的。但是,他们没有为这种特殊情况命名。

在没有这个特定结构的独特名称的情况下,Scott Meyers coined the widely known term/name universal references

然而,出于多种原因,委员会认为这个名称不合适。因此,提案 N4164 made by Herb Sutter, Bjarne Stroustrup and Gabriel Dos Reis 建议将名称更改为 Forwarding References

名称转发参考文献 在委员会成员(包括前面提到的提案的作者)的非正式讨论中获得最多支持。有趣的是,正是 Scott Meyers 本人在他最初的“通用参考”演讲中引入了该术语。然而,后来他决定使用 universal references 这个名字。对于这个决定起作用的事实是,当时他不认为 转发引用 一词还包括 auto&& 案例。

为什么不通用引用

根据提议,通用引用这个词虽然是一个合理的名字,意思也很明显,但在几个方面恰好是错误的。

一个通用参考必须表示如下:

  • 随处可用的参考;或
  • 万物皆可参考;或
  • 类似的东西。

显然,情况并非如此,这种结构的使用也不恰当。此外,这个名称会鼓励许多人认为具有这样名称的东西是要使用的 "universally"。委员会认为这是一件坏事。

此外,"universal references"甚至都不是 引用本身,而是一组规则,用于在特定上下文中以特定方式使用引用,并具有对该用途的某种语言支持,并且该用途是转发

为什么 auto&& 也被视为转发案例

auto&& 也被认为是前向案例,因为它遵循 reference collapsing rules。例如:

  • 形式为 [](auto&& x){ … }
  • 的通用 lambda
  • for-形式的范围循环,for(auto &&i : v) { ... }
  • 最后,总的来说auto&&局部变量是为了转发

转发引用的标准措辞

术语转发引用draft standard N4527中的以下位置被提及:

§14.8.2.1/ 从函数调用中推导模板参数 [temp.deduct.call] (强调我的):

If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. A forwarding reference is an rvalue reference to a cv-unqualified template parameter. If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction. [ Example:

template <class T> int f(T&& heisenreference);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue

— end example ]

§14.8.2.5/p10 从类型推导模板参数 [temp.deduct.type]:

Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-typelist of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If P and A are function types that originated from deduction when taking the address of a function template (14.8.2.2) or when deducing template arguments from a function declaration (14.8.2.6) and Pi and Ai are parameters of the top-level parameter-type-list of P and A, respectively, Pi is adjusted if it is a forwarding reference (14.8.2.1) and Ai is an lvalue reference, in which case the type of Pi is changed to be the template parameter type (i.e., T&& is changed to simply T). [ Note: As a result, when Pi is T&& and Ai is X&, the adjusted Pi will be T, causing T to be deduced as X&. — end note ] [Example:

template <class T> void f(T&&);
template <> void f(int&) { } // #1
template <> void f(int&&) { } // #2
void g(int i) {
f(i); // calls f<int&>(int&), i.e., #1
f(0); // calls f<int>(int&&), i.e., #2
}

— end example ] If the parameter-declaration corresponding to Pi is a function parameter pack, then the type of its declaratorid is compared with each remaining parameter type in the parameter-type-list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. During partial ordering (14.8.2.4), if Ai was originally a function parameter pack: