我们知道一个数组的大小一定是一个
constant expression
。
我们也知道 constant expression
可以在编译时求值,但不能保证它会在编译时求值。
所以现在......假设
foo()
可用作常量表达式:
如果编译器(无论出于何种原因,真的)在这个假设的场景中没有在编译时评估 foo()
调用,会发生什么?数组将如何“初始化”?这会是 VLA 的情况吗?或者它会被认为是“未知边界的数组”?
int myArray[foo()]; // assume the compiler doesn't evaluate foo() at compile time, because it's not mandatory...?
编辑:看这个例子:
constexpr int foo() {
// constexpr: CAN be evaluated at compile time
// Not mandatory for it to BE evaluated at compile time (??)
return 5;
// A conforming compiler might still decide to evaluate this at run-time, no?
}
int main() {
// A compiler might decide to NOT evaluate foo() at compile time
// So what...?
int myArray[foo()];
};
写作时
int myArray[foo()];
这是声明具有已知边界的数组的语法形式。标准规定在这样的声明中用于数组大小的表达式必须是一个 converted constant expression of type
std::size_t
.
如果不是这样的常量表达式,那么程序是病式的。该标准要求编译器发出诊断,并且不需要任何关于此类程序行为的其他信息。
如果是这样的常量表达式,则指定声明将
myArray
声明为大小为X的int
的array类型的对象,其中X
是foo()
的(转换)值。
标准的其余部分描述了给定大小的数组的行为方式,例如如何访问索引
0
到 X-1
处的元素,它们的 sizeof
是 X
等等。编译器必须确保执行程序的行为符合根据这些规则的标准描述。
这就是常量表达式的全部含义。实施不需要其他任何东西。
如何指定数组的方式意味着如果编译器想要有效地实现数组,它实际上需要知道常量表达式
foo()
的值。例如,否则它将无法在堆栈上保留足够的内存,否则将需要做一些低效的事情,例如使用运行时计算值调用alloca
。编译器没有理由要这样做。
另外,因为编译器需要诊断
foo()
是否是常量表达式,这本身就有效地需要对表达式进行全面评估,如果编译器不存储计算值而不是在运行时重新计算它来实现,那将是愚蠢的一些不寻常的低效形式或数组。
归根结底,数组的正常实现,数组的大小根本不是运行时存在的量。数组的大小始终由其声明中的常量表达式固定。在格式良好的 C++ 程序中,同一个数组声明不能有两种不同的大小。因此,在实现中保持变量也是没有意义的。
此外,标准 C++ 中没有 VLA,并且声明不使用未绑定数组的形式,因此两者都不是。它只是一个大小为
5
. 的数组的声明