什么语言规则规定模板化数组&&函数参数中的“T&&”不是转发引用?

dfr*_*fri 5 c++ templates language-lawyer forwarding-reference

我正在使用的静态分析工具提示我需要std::forward调用链中以下函数的参数:

template<typename T, std::size_t size>
constexpr void f(T (&& arr)[size]) { /* linter: use std::forward on 'arr' here */ }
Run Code Online (Sandbox Code Playgroud)

因为它将函数参数类型标识为转发引用。

然而,对多个编译器的简单测试表明,这不是转发引用。下面的例子

void g() {
    int a[3]{1, 2, 3};
    f(a);  // #1
}
Run Code Online (Sandbox Code Playgroud)

#1在(DEMO )处被拒绝,明确指出函数参数是右值引用:

 error: cannot bind rvalue reference of type 'int (&&)[3]' to lvalue of type 'int [3]'
Run Code Online (Sandbox Code Playgroud)

问题

  • 哪些语言规则控制着上面T&&的模板化数组函数&&参数f不是转发引用?

dfr*_*fri 3

相关部分是[temp.deduct.call]/1 [重点是我的]:

\n
\n

模板参数推导是通过将包含参与模板参数推导的模板参数的每个函数模板参数类型(称为P)与调用的相应参数类型(称为A)进行比较来完成的,如下所述。如果从\n 中删除引用和 cv 限定符给出Por std::initializer_list<P\xe2\x80\xb2>对于P\xe2\x80\xb2[N]某些P\xe2\x80\xb2\nN并且参数是非空初始化器列表\n([dcl.init.list]),则对初始化器的每个\n元素执行推导独立列出,将P\xe2\x80\xb2单独的函数模板参数类型P\xe2\x80\xb2ii第一个初始化元素作为相应的参数。[...]

\n
\n

含义[temp.deduct.call]/3不适用于P最初是数组引用类型的 a (一旦达到 /3,/3 适用于每个元素)。

\n

但是,如果我们返回到上面的部分,我们可能会注意到它带有限制

\n
\n

[...]并且参数是一个非空的初始值设定项列表[...]

\n
\n

对于 OP 的示例函数来说,意味着一个调用f,例如

\n
f({1, 2, 3});  // [temp.deduct.call]/1 applies\n
Run Code Online (Sandbox Code Playgroud)\n

但是如果参数不是初始化列表而是数组呢?

\n
int arr[3]{1, 2, 3};\nf(arr);\n
Run Code Online (Sandbox Code Playgroud)\n

[temp.deduct.call]/2对于数组有特殊规则,但它并不像P引用类型一样适用,这意味着我们最终到达[temp.deduct.call]/3

\n
\n

如果 P 是 cv 限定类型,则类型推导时将忽略 P 类型的顶级 cv 限定符。如果 P 是引用类型,则使用 P 引用的类型进行类型推导。

\n

转发引用是对 cv 不合格模板参数的右值引用,该模板参数不表示类模板的模板参数(在类模板参数推导 ([over.match.class.deduct]) 期间)。如果 P 是转发引用并且参数是左值,则使用对 A\xe2\x80\x9d 的类型 \xe2\x80\x9clvalue 引用代替 A 进行类型推导。

\n
\n

然而,中的函数参数f是对 的右值引用T[size],它不是模板参数。这控制了参数不是初始值设定项列表时的情况(也可以说是这种情况)。

\n