内置数组大小的类型是啥?

Posted

技术标签:

【中文标题】内置数组大小的类型是啥?【英文标题】:What is the type of a built-in array's size?内置数组大小的类型是什么? 【发布时间】:2014-07-18 13:26:27 【问题描述】:

当推导非类型模板参数时,比如nn 的类型必须在参数和实参之间完全匹配。所以下面的代码不会编译(至少在 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]);
...

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

【问题讨论】:

错误信息是什么? 隐式积分转换并不是什么新鲜事... @Lightness Races in Orbit:虽然这对于非类型模板参数的类型来说是新事物。 【参考方案1】:

来自 §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:

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

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

转换后的 T 类型常量表达式是一个表达式,隐式转换为 T 类型的纯右值,其中转换后的表达式是核心常量表达式,并且隐式转换序列仅包含用户定义的转换、左值到右值的转换(4.1)、积分促销 (4.5) 和积分转化 (4.7),而不是缩小转化 (8.5.4)。

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

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

如果绑定的值适合您的参数类型,它将成功转换。如果没有,您最终会得到一个实现定义的边界值。

数组是一种特殊情况,正如ectamur's answer 中指出的那样:

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

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

【讨论】:

14.8.2.5 触发一切 :-) 内置数组和 C++ 中的数组一样特别。【参考方案2】:

这在 14.8.2.5 [temp.deduct.type] 第 17 段中有所介绍:

17 - [...] [The] 模板参数类型应与模板参数的类型完全匹配,除了从数组绑定推导出的模板参数可以是任何整数类型。

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

17 - 如果P 有一个包含&lt;i&gt; 的表格,并且如果A 对应值的类型与i 的类型不同,则推演失败。如果P有一个形式包含[i],而i的类型不是整数类型,则推演失败。

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

确实,在标准的 C++11 版本中没有任何地方为数组边界指定首选类型;一个数组边界被指定(在[dcl.array]中)为“一个整数常量表达式,其值应大于零”。在最近的 C++14 草案中,这被修改为“std::size_t [...] 类型的转换常量表达式 (5.19)”;更改后的定义可以追溯到n3306。有点奇怪的是,这种变化被描述为“为了一致性而进行的后续调整 [..]”,这意味着编辑认为 size_t 是正确的类型是不言而喻的。 p>

【讨论】:

考虑到sizeof运算符的返回类型,我觉得有点明显:-)【参考方案3】:

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

template <typename T, char n > void f(T (&)[n]) 

int main()  
  int a[1000];
  f(a);

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

【讨论】:

请记住这是来自&lt;cstddef&gt;std::size_t 您的帖子没有回答问题。 OP 没有询问容器使用什么尺寸类型,而是询问为什么可以在问题中提出的上下文中使用 size_t 以外的类型。 也就是说,在非类型模板参数推导中不允许进行窄化转换。

以上是关于内置数组大小的类型是啥?的主要内容,如果未能解决你的问题,请参考以下文章

我无法在 Java 中创建泛型数组类型的原因是啥?

java语言中集合与数组的区别是啥?

C语言中“SIZEOF(INT)“是啥意思?有啥作用?

JAVA里面set,map,list的区别是啥?(尽量详细)

javascript中new是啥意思

集合 和 数组 的区别