为什么数组类型不会衰减到类模板的指针

Why does array type not decay to pointer for class templates?

本文关键字:指针 衰减 数组 类型 为什么      更新时间:2023-10-16

我们知道数组衰减到函数模板中的指针,并且要获取数组类型参数,我们需要使用对数组的引用来声明我们的函数模板:

template<class T, std::size_t N>
std::size_t number_of_elements(T (&ary)[N]) {
  return N;
}

但是,为什么我们不需要在类模板中声明对数组参数的引用?下面的代码显示了这一点,并在 C++11 下编译。

template<class T>
struct cls_number_of_elements {};
template<class T, std::size_t R>
struct cls_number_of_elements<T[R]> {
  static const int N = R;
};
char ary[] = "12345";
auto const N = cls_number_of_elements<decltype(ary)>::N;
char ar2[N];

您所说的"衰减"是从参数推断函数模板参数时发生的事情。 我在这里发布了更全面的解释。

为模板参数显式提供值时,没有扣除步骤;显式提供的值正是参数采用的值。

对于类模板,永远不会有参数推导;它们必须始终显式提供其参数。

说明性示例:

template<typename T> void f(T t) {}
template<typename T> struct S { void f(T t) {} };
...
int x[27];
f(x);               // Type deduction: decay occurs, T = int *
f<int *>(x);        // No deduction. T = int *
f<int[27]>(x);      // No deduction. T = int[27]
S<int[27]>().f(x);  // No deduction. T = int[27]

在后两种情况下,调整仍然发生。[temp.deduct]/3 明确重申了这一点:当 T 是数组类型时,函数参数 T t 表示t具有指针类型,其方式完全相同:

void g(int t[27])

实际上指定t具有类型 int *

来自 C++11 标准:

7.1.6.2 简单类型说明符

4 decltype(e)表示的类型定义如下:

— 如果e是无括号的 ID 表达式或无括号的类成员访问 (5.2.5(,则decltype(e) 是由 e 命名的实体的类型。如果没有这样的实体,或者如果e命名一组重载函数,则程序格式不正确;

— 否则,如果e是 x值,则decltype(e) T&&,其中Te的类型;

— 否则,如果e是左值,则decltype(e) T&,其中Te的类型;

— 否则,decltype(e)e的类型。

鉴于

char ary[] = "12345";

decltype(ary)表示ary的类型(一个不带括号的 id 表达式(,即 char[6]

有关decltype的更用户友好的描述,请参阅 http://en.cppreference.com。

在 C 和 C++ 标准之间,这个答案看起来解释是:

4.2 数组到指针的转换

  1. "N T数组"或"未知T界数组"类型的左值或右值可以转换为右值 类型为"指向T的指针"。结果是指向数组的第一个元素的指针。

看起来这种转换确实发生在函数模板中推导的模板参数

number_of_elements(ary)

不适用于类模板中的模板参数

cls_number_of_elements<char[5]>

和显式类型函数模板

number_of_elements<char[5]>(ary)

因为没有推断出这些类型。

查看代码,我有一个非常简短的解释,这与类和函数模板之间的差异没有任何关系:

对于number_of_elements数组的"类型"是 T[N] 。示例适用于"类型"T[R] cls_number_of_elements。在这两种情况下,T都会变得char,而N会恢复。 R变得6.需要&的唯一原因是无法按值传递 c 数组。(见@bku_drytt的安瑟(