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

What language rules governs that `T&&` in a templated array-by-&& function argument is *not* a forwarding reference?

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

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

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

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

void g() {
    int a[3]{1, 2, 3};
    f(a);  // #1
}

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

 error: cannot bind rvalue reference of type 'int (&&)[3]' to lvalue of type 'int [3]'

问题

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

Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list<P′> or P′[N] for some P′ and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list independently, taking P′ as separate function template parameter types P′i and the i:th initializer element as the corresponding argument. [...]

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

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

[...] and the argument is a non-empty initializer list [...]

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

f({1, 2, 3});  // [temp.deduct.call]/1 applies

但是参数不是初始化列表而是数组的情况呢?

int arr[3]{1, 2, 3};
f(arr);

[temp.deduct.call]/2 have special rules for arrays, but it does not apply as P is a reference type, meaning we do end up at [temp.deduct.call]/3:

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 that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). 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.

但是,f 中的函数参数是对 T[size] 的右值引用,它不是模板参数。这适用于参数不是初始化列表的情况(并且可以说也是这种情况)。