c++ - 在模板参数中,哪些规则允许编译器推断数组的项数?

给定以下两个程序——除了模板函数 len() 的定义外完全相同:

// ================================
//  a.cc
// ================================
#include <iostream>

template<class T, std::size_t n>
std::size_t len(T (&v)[n]) { return n; }

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

    std::cout << len(arr) << std::endl;
}
// ================================
//  b.cc
// ================================
#include <iostream>

template<class T, std::size_t n>
std::size_t len(T v[n]) { return n; }

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

    std::cout << len(arr) << std::endl;
}

使用 g++ 时,第一个程序按预期编译和运行。但是第二个不编译。这是诊断:

b.cc: In function ‘int main()’:
b.cc:13:25: error: no matching function for call to ‘len(int [3])’
     std::cout << len(arr) << std::endl;
                         ^
b.cc:7:13: note: candidate: template<class T, long unsigned int n> std::size_t len(T*)
 std::size_t len(T v[n]) { return n; }
             ^
b.cc:7:13: note:   template argument deduction/substitution failed:
b.cc:13:25: note:   couldn't deduce template parameter ‘n’
     std::cout << len(arr) << std::endl;

为什么编译器能够在第一种情况下推断出数组中的项目数,而在第二种情况下却不能?在 C++11 或 C++17 标准中,是什么导致必须编写 T (&v)[n] 而不是 T v[n]

最佳答案

[temp.deduct] 涵盖了模板参数推导[强调我的]:

/1 When a function template specialization is referenced, all of the template arguments shall have values. The values can be explicitly specified or, in some cases, be deduced from the use or obtained from default template-arguments.

/2 (... regarding explicit template argument list: not relevant here)

/3 After this substitution is performed, the function parameter type adjustments described in [dcl.fct] are performed. [ Example: A parameter type of “void (const int, int[5])” becomes “void()(int,int)”. — end example ] [...]

注意对[dcl.fct]的引用和/3中的相关(非规范)示例,显示了对值类型数组函数参数的调整int[N]int* ,表示非类型模板参数 N不能从传递给值类型数组参数的参数中推断出来(N 在此上下文中无用/被忽略)。

函数调用的模板参数推导,特别是在 [temp.deduct.call] 中;区分您的两个示例的相关部分是 [temp.deduct.call]/2.1 ,它表示如果调用的参数(表示为 A )是数组类型,并且参数类型(表示为 P )不是引用类型,则指针类型用于类型推导:

/2 If P is not a reference type:

  • (2.1) If A is an array type, the pointer type produced by the array-to-pointer standard conversion is used in place of A for type deduction; otherwise, [...]

而当P 引用类型,如下例所示:

template<class T, std::size_t n>
std::size_t len(T (&v)[n]) { return n; }

[temp.deduct.call]/2.1 不适用,并且对于以下对名为 len 的函数的调用以数组作为参数

int arr[] = { 1,2,3 };
(void)len(arr);

名称查找会找到函数模板len , 模板参数推导随后将应用于 P作为T[N] (根据 [temp.deduct.call]/3 )和 A作为int[3]对于len的单一功能参数, 推导出 TintN3对于参数类型的完整类型推导 P ;根据 [temp.deduct.type]/1 :

Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.

即非类型模板参数N是单个函数参数的 type 的一部分(类型模板参数 T 也是如此)。

https://stackoverflow.com/questions/65619713/

相关文章:

c++ - C++ 是否包含包含的头文件包含的所有头文件?

javascript - JavaScript 中的常量数组

python - PyTorch 张量的零对角线?

powershell - 在 switch 语句中分配变量

reactjs - React 项目中的 URI 格式错误 - 使用 Parcel Bundler

c++ - fatal error LNK2019 C++,Unreal Engine Build

database - 如何在 Jetbrain 的 Datagrip 中以字符串 UUID 格式显示

python - 从明亮的彩条下发现图像

.htaccess - 使用 Apache 在 DDEV 中重定向 HTTP -> HTTPS

reactjs - 接口(interface)命名约定(接口(interface)名称与组件名称冲突