我可以在堆栈上创建一个可变大小的C风格数组,没有任何问题:
void f(int n) {
float data1[n]; // OK
}
但我不能对std::array
执行相同的操作:
void f(int n) {
std::array<float,n> data2; // error: ‘n’ is not a constant expression
}
以下示例更全面地说明了我的观点:
#include <array>
#include <cstdio>
template<int N>
class A{
public:
void print() {
printf("data[0]: %2.2f\n", data[0]);
}
private:
float data[N];
};
void f(int n) {
float data1[n]; // OK
std::array<float,n> data2; // error: ‘n’ is not a constant expression
A<n> data3; // error: ‘n’ is not a constant expression
}
int main(int argc, char **argv) {
f(3);
}
我的问题:有没有办法在堆栈上严格创建一个可变大小的类/结构?如果是这样,怎么样?如果没有,这仅仅是因为规范还是因为技术原因而被禁止?
答案 0 :(得分:4)
您正在将苹果与橙子进行比较。
当你使用variable-length array时,你会引用一个来自旧时代的特性(在C99中引入),它允许在堆栈上分配数组,而不需要在编译时知道大小。这是由编译器完成的,只需在运行时保留所需的堆栈大小,而不是预先计算位移。
如果我没记错的话,它实际上已经在C ++ 11中降级为可选功能状态(而C11带来了它)。
然后你有std::array<T,size_t>
这是完全不同的东西。使用编译时类型/值来实例化template
和是必需的。所以你根本无法做你想做的事。
重点是C ++是一种允许您避免使用VLA的语言,因为您有std::vector
和动态分配。
如果你真的想在堆栈上分配东西,你必须寻找99.9%的时间不需要特定和肮脏的技巧,比如放置新的alloca
(在任何情况下都是非标准的) ,例如:
void *reserved = alloca(sizeof(MyClass));
new (reserved) MyClass();
答案 1 :(得分:3)
std::array
不是“可变大小的类/结构”。这是一个模板。它碰巧实现了一个数组容器,其长度由第二个模板参数指定,但这与“一个可变大小的类/结构”不同。
std::array
模板的第二个参数必须是常量值。模板参数可以是类,也可以是常量值。它不能是非常数值。这就是模板的工作方式。
您可能想要的是std::vector
。声明它,并resize()
它。这是你的大小不一的数组。
答案 2 :(得分:3)
有没有办法在堆栈上严格创建一个可变大小的类/结构?
没有。 C ++标准中不支持可变大小的class
/ struct
,因此无法在任何地方创建它们。也就是说,您可以创建一个class
/ struct
,其中最终数据成员是一个数组,并故意超出数组维度。这个技巧在C中被称为“结构黑客” - 请参阅FAQ。它的未定义行为是否取决于您的实现。您可以将其与alloca
和展示位置 - new
结合使用,以在堆栈上创建此类变量,但如果您的程序依赖于任何一方,则应谨慎地手动delete
析构函数的影响。
如果没有,这仅仅是因为规格还是因为技术原因而被禁止?
原因是表现。通常,可以使用堆栈指针CPU寄存器加上常量偏移来访问堆栈上的变量,编译器可以在编译器时为任何给定的本地/自动变量计算。当堆栈上有可变长度内容时,之后的局部变量不再处于堆栈指针寄存器的固定偏移量,这意味着编译器必须生成代码以添加各种值,以便计算访问时的运行时偏移量变量。当您有多个可变长度的堆栈托管变量时,问题会复合,性能会进一步降低。为了避免程序员产生意外缓慢的代码,并使编译器更简单,标准不需要支持可变长度类型。