为什么C ++不支持基于动态数组循环的范围?就是这样:
int* array = new int[len];
for[] (int i : array) {};
我刚发明for[]
语句与new[]
和delete[]
押韵。据我所知,运行时具有可用数组的大小(否则delete[]
无法工作),因此理论上,基于for循环的范围也可以使用。它不起作用的原因是什么?
答案 0 :(得分:12)
它不起作用的原因是什么?
基于范围的循环,如
for(auto a : y) {
// ...
}
只是以下表达式的语法糖
auto endit = std::end(y);
for(auto it = std::begin(y); it != endit; ++it) {
auto a = *it;
// ...
}
由于std::begin()
和std::end()
不能与普通指针一起使用,因此不能使用分配有new[]
的指针来应用。
据我所知,运行时具有可用数组的大小(否则
delete[]
无效)
delete[]
如何跟踪用new[]
分配的内存块(其大小不一定与用户指定的大小相同),这是完全不同的事情,编译器很可能甚至不知道这是如何实现的。
答案 1 :(得分:6)
当你有这个:
int* array = new int[len];
这里的问题是你的变量array
根本不是一个数组。它是指针。这意味着它只包含一个对象的地址(在本例中是使用new
创建的数组的第一个元素)。
对于基于工作的范围,编译器需要两个地址,即数组的开头和结尾。
所以问题是编译器没有足够的信息来执行此操作:
// array is only a pointer and does not have enough information
for(int i : array)
{
}
答案 2 :(得分:3)
这与动态数组无关,它更为通用。当然,对于动态数组,存在一个可以调用析构函数的大小(但请记住,标准并没有说明任何内容,只是调用delete []
按预期工作)。
问题在于指针通常是指针指示你不能告诉它是否对应于任何类型的......什么?
数组衰减到指针,但给出一个指针,你能说什么?
答案 3 :(得分:3)
int* array = new int[len]; for[] (int i : array) {}
必须解决几点问题;我会一次解决一个问题。
运行时是否知道数组的大小?
在某些条件下,它必须。正如您所指出的,对delete[]
的调用将调用每个元素的析构函数(按预留顺序),因此必须知道有多少元素。
但是,通过不指定必须知道和访问元素的数量,C ++标准允许实现在不需要调用析构函数时省略它(std::is_trivially_destructible<T>::value
求值为{{1} })。
运行时可以区分指针和数组吗?
一般来说,没有。
当你有一个指针时,它可以指向任何东西:
这就是true
存在的原因,在这里使用delete[]
会有误。使用delete
,用户说明:此指针指向堆分配的数组的第一项。
然后,实现可以假设,例如,在第一个项之前的8个字节中,它可以找到数组的大小。如果不保证这一点,那8个字节可以是任何东西。
那么,为什么不一直走下去创建
delete[]
?
有两个原因:
for[] (int i : array)
语法,在每种类型的基础上将不再可能。说实话,for[]
和new[]
是旧时代的遗物。他们非常尴尬:
并且使用不安全:
在现代C ++中,通常没有理由使用delete[]
和new[]
。大多数时候应该首选delete[]
;在容量多余的少数情况下,std::vector
仍然更好(因为它跟踪大小)。
因此,如果没有合理的理由继续使用这些语句,就没有动力去包含专门用于处理它们的新语义结构。
任何人都应该有足够的动力来提出这样的建议:
std::dynarray
),也可能会对它们持有。< / LI>
我建议您只使用std::variant
。
答案 4 :(得分:1)
array
不是数组,而是指针,并且没有关于“数组”的大小的信息。因此,编译器无法推断出此数组的begin
和end
。
请参阅range based for循环的语法:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr, __end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
range_expression - 表示合适序列的任何表达式 (开头和结束成员函数的数组或对象) 或定义了自由函数,见下文)或braced-init-list。
auto
在编译时工作。因此,begin_expr
和end_expr
不会扣除运行时间。
答案 5 :(得分:1)
原因是,只给出指针array
的值,编译器(和你的代码)没有关于它指向的信息。唯一知道的是array
的值是单个int
的地址。
它可以指向静态分配的数组的第一个元素。它可以指向动态分配的数组中间的元素。它可以指向数据结构的成员。它可以指向数据结构中的数组元素。名单还在继续。
您的代码将针对指针指向的内容进行假设。它可以假设它是一个由50个元素组成的数组。您的代码可以访问len
的值,并假设array
指向len
元素数组的(第一个元素)。如果您的代码正确,则所有代码都按预期工作。如果您的代码出错(例如,访问具有5个元素的数组的第50个元素),那么行为就是未定义的。它是未定义的,因为可能性是无穷无尽的 - 用于跟踪任意指针实际指向的内容(超出该地址处有int
的信息)将是巨大的。
您从array
指向new int[len]
结果的假设开始。但是该信息不存储在array
本身的值中,因此编译器无法返回值len
。这将是您的&#34;基于范围的&#34;工作方法。
虽然,是的,给定array = new int[len]
,delete [] array
调用的机制将确定array
具有len
个元素,然后释放它们。但如果delete [] array
来自array
表达式以外的其他内容,则new []
也会有未定义的行为。甚至
int *array = new int;
delete [] array;
给出未定义的行为。 &#34;运行时&#34;在这种情况下,array
实际上是单个动态分配的int
(而不是实际数组)的地址。所以不需要处理这个问题。