为什么不可变长度数组是C++标准的一部分?

在过去的几年里,我很少使用C语言。当我今天读到这个问题时,我遇到了一些我不熟悉的C语法

显然,在C99中,以下语法是有效的:

无效foo(int n){
int值[n];//声明一个可变长度数组
}

这似乎是一个非常有用的功能。有没有讨论将它添加到C++标准中,如果是,为什么省略了?

一些潜在原因:

  • 编译器供应商难以实现的问题
  • 与标准的其他部分不兼容
  • 功能可以用其他C++构造仿真

C++标准声明数组大小必须是常数表达式(83.4.1)。

是的,我当然意识到在玩具示例中可以使用std::vector<int>数值(m),但这将从堆而不是堆栈分配内存。如果我想要一个多维数组,比如:

void foo(整数x,整数y,整数z){
int值[x][y][z];//声明一个可变长度数组
}

vector版本变得非常笨拙:

void foo(整数x,整数y,整数z){
vector<vector<vector<int>>值(/*此处的表达式非常痛苦。*/);
}

片、行和列也可能分布在内存中

看看comp.std.c++上的讨论,很明显,这个问题很有争议,争论双方都有一些非常重要的名字。显然,std::vector总是一个更好的解决方案

(背景:我有一些实现C和C++编译器的经验)

C99中的可变长度数组基本上是一个错误。为了支持VLA,C99必须根据常识做出以下让步:

  • sizeof x不再总是编译时常量;编译器有时必须生成代码以在运行时计算sizeof-表达式

  • 允许二维VLA(inta[x][y])需要一种新的语法来声明将二维VLA作为参数的函数:void foo(intn,inta[][*])

  • L> >在C++世界中,对C的嵌入式系统程序员的目标受众来说非常重要,声明VLA意味着要剔除堆栈中的任意大的块。这是一个保证的堆栈溢出和崩溃。(每当您声明inta[n]时,您都会隐式地断言您有2GB的堆栈可供使用。毕竟,如果您知道“n这里肯定小于1000”,那么您只需声明inta[1000]。将32位整数n替换为1000就是承认您不知道程序的行为应该是什么。)

<好了,现在我们来讨论C++。在C++中,我们在C89所做的“类型系统”和“价值系统”之间有着同样的强烈区别……但是我们确实开始依赖于C没有的方式。例如:

模板&lt;typename T&gt;结构S{…};
int A[n];
S&lt;decltype(A)&gt;s、 //等效地,S&lt;整数[n]&gt;s

如果n不是编译时常量(即a是可变修改类型),那么S到底是什么类型?S的类型是否也只能在运行时确定

那么这个呢:

模板&lt;typename T&gt;boolmyfunc(T&amp;t1,T&amp;t2){…};
int A1[n1],A2[n2];
myfunc(A1,A2);

编译器必须为myfunc的某些实例化生成代码。代码应该是什么样子?如果我们在编译时不知道A1的类型,我们如何静态生成该代码

更糟糕的是,如果在运行时发现n1!=n2,所以!标准::是否相同&lt;decltype(A1)、decltype(A2)&gt;()?在这种情况下,对myfunc的调用甚至不应该编译,因为模板类型推断应该失败!我们怎么可能在运行时模仿这种行为呢

基本上,C++正朝着越来越多的决策方向发展到编译时间:模板代码生成、代码> CONTXPRP功能评估等。与此同时,C99忙于将编译时的决策(例如,sizeof)推送到运行时。考虑到这一点,是否真的有意义的花费EPM>尝试将C99风格VLAS集成到C++?

正如其他回答者已经指出的那样,C++提供了大量堆分配机制( STD: lutuxpTr& lt;int []],a = new int [n]; >或 STD::vector & lt;int(gn);(a),(是显而易见的)当你真的想传达“我不知道我需要多少RAM”的想法。C++提供了一个极好的异常处理模型,用于处理你所需要的RAM数量大于你所拥有的RAM数量的情况。但希望 >这个答案让您了解为什么C99样式VLAS是“强>不


有关此主题的更多信息,请参阅N3810“阵列扩展的替代方案”,比亚恩·斯特劳斯特鲁普2013年10月关于VLAs的论文。比亚恩的POV和我的非常不同;N38的重点是寻找一个好的C++ ISH语法/e>,并且在C++中阻止使用原始数组,而我更多地关注元编程和类型系统的含义。我不知道他是否认为元编程/类型系统的含义已经解决、可以解决,或者仅仅是无趣


一篇很好的博文提到了许多相同的观点,“可变长度数组的合法使用”(Chris Wellons,2019-10-27)

发表评论