内置数组的大小是什么类型?

What is the type of a built-in array's size?

本文关键字:是什么 类型 数组 内置      更新时间:2023-10-16

在推导非类型模板形参时,例如n, n的类型必须在形参和实参之间完全匹配。因此,以下代码将无法编译(至少在GCC和clang上):

#include <array>
template <int n> void f(std::array<int, n> const&) {}
int main() {
  std::array<int, 3> arr;
  f(arr);
}

这是因为std::array声明为

template <typename T, std::size_t n> class array;

而不是

template <typename T, int n> class array;

然而,要捕获内置数组的大小,似乎任何整型都可以。以下所有关于GCC、clang和vc++的工作:

template <typename T, char n > void f(T (&)[n]);
template <typename T, short n> void f(T (&)[n]);
template <typename T, int   n> void f(T (&)[n]);
...

那么,说真的,内置数组的大小类型是重载的?

从§8.3.4 [dcl.array]/1:

如果常量表达式(5.19)则它必须是std::size_t类型的转换后的常量表达式,且其值必须大于零。常量表达式指定数组

的范围(元素个数)。

类型为std::size_t

其他类型通常工作的原因是,根据§14.3.2 [temp.arg.nontype]/5:

,转换是在传入的类型上完成的。

对于整型或枚举型的非类型模板形参,应用已转换常量表达式(5.19)中允许的转换。

出自§5.19 [expr.const]/3:

整型常量表达式是整型或无作用域枚举类型的表达式,隐式转换为右值,其中转换后的表达式是核心常量表达式。

snipp (提到这些可以用作数组边界)

转换后的T类型常量表达式是隐式转换为T类型右值的表达式,其中转换后的表达式是核心常量表达式,隐式转换序列只包含用户定义转换、左值到右值转换(4.1)、整型提升(4.5)和整型转换(4.7),而不包含窄化转换(8.5.4)。

最后,§4.7 [convr .integral]/3:

如果目的类型是有符号的,如果该值可以用目的类型(和位域宽度)表示,则该值不变;否则,该值为实现定义。

如果绑定值符合参数类型,则转换成功。否则,您将以实现定义的绑定值结束。

数组是一种特殊情况,正如ectamur的回答所指出的:

§14.8.2.5 [temp.deduct.type]/17:

如果在具有非类型模板形参的函数模板声明中,非类型模板形参在函数形参列表的表达式中使用,并且如果推导出相应的模板实参,则模板实参的类型必须与模板形参的类型完全匹配,但从数组绑定中推导出的模板实参可以是任意整型。

这在14.8.2.5 [temp.deduct.type]第17段中有涉及:

17 -[…]模板实参的类型必须与模板形参的类型完全匹配,但从数组中导出的模板实参可以是任意整型。

在http://wg21.cmeerw.net/cwg/issue1770中,它被改进为更通用的:

17 -如果P有包含<i>的形式,且A对应值的类型与i不同,则推导失败。如果P的形式包含[i],并且i的类型不是整型,则推导失败。

因此,数组边界可以推导为任何整型,但非类型模板形参必须推导为模板定义中的实际类型。

实际上,c++ 11版本的标准中没有任何地方指定数组边界的首选类型;数组边界(在[dcl.array]中)指定为"一个整型常量表达式,其值必须大于零"。在c++ 14的最新草案中,这被修改为"类型为std::size_t[…]的转换常量表达式(5.19)";修改后的定义可以追溯到n3306。有点奇怪的是,这种变化被描述为"相应的调整[…]]为了一致性",暗示编辑认为size_t是正确的类型是不言而喻的。

C数组的大小是编译时常量,由编译器管理(运行时没有关于C数组大小的信息),当你实例化模板时,编译器只检查实际大小是否适合提供的模板类型。例子:

template <typename T, char n > void f(T (&)[n]) {}
int main() { 
  int a[1000];
  f(a);
}

即使int a[20];成功,这个也会失败。