内置数组大小的类型是啥?
Posted
技术标签:
【中文标题】内置数组大小的类型是啥?【英文标题】:What is the type of a built-in array's size?内置数组大小的类型是什么? 【发布时间】:2014-07-18 13:26:27 【问题描述】:当推导非类型模板参数时,比如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]);
...
所以说真的,内置数组大小的类型是重载的?
【问题讨论】:
错误信息是什么? 隐式积分转换并不是什么新鲜事... @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
有一个包含<i>
的表格,并且如果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];
成功,这也会失败。
【讨论】:
请记住这是来自<cstddef>
的std::size_t
。
您的帖子没有回答问题。 OP 没有询问容器使用什么尺寸类型,而是询问为什么可以在问题中提出的上下文中使用 size_t
以外的类型。
也就是说,在非类型模板参数推导中不允许进行窄化转换。以上是关于内置数组大小的类型是啥?的主要内容,如果未能解决你的问题,请参考以下文章