C++中的基于范围的for循环

5
似乎 C++11 中提供的“for each”语法可以在不知道数组(元素数量)实际大小的情况下进行数组迭代。我认为,既然它是新标准的一部分,那么即使对于C数组来说,这也是完全安全的。通常,在操作 C 数组之前还必须单独知道其大小,但我希望有经验的人验证一下这个新的 C++ 技术是否正如您所期望的那样工作:
extern float bunch[100];

for (float &f : bunch) {
  f += someNumber;
}

这种技术是否存在一些不显而易见的副作用或缺陷?我在看到的代码中没有看到太多信息,很可能是因为大部分代码是在此技术成为标准之前编写的。我想确保其稀有的使用并不是由于其他不为人知的原因。


10
你总是可以知道数组的大小(https://gist.github.com/3959946#file-arrays-h-L33)。如果将其转换为指针,你就无法知道大小了。 - R. Martinho Fernandes
1
@R.MartinhoFernandes:不错的代码片段! - Matthieu M.
2
一般来说,如果放弃使用普通数组并转向使用标准库容器,你的C++编程生活会更加愉快。 - Harald Scheirich
3个回答

4

这种用法并不奇怪或者不安全。数组的大小在编译时已知,因此可以在循环中使用。这是一个模板函数的例子,它允许您找出数组的长度:

template< class T, std::size_t N >
std::size_t length( const T (&)[N] )
{
  return N;
}

Foo f[5];
std::cout << length(f) << "\n";

这应该清楚地表明,您不能在动态大小的C风格数组上使用此技术或基于范围的循环。

当然,如果您有范围基础的循环,则应该还有std :: array(如果没有,则可以从std :: tr1或boost中获取),因此您可以完全避免使用C风格数组:

extern std::array<float, 100> bunch;

for (auto &f : bunch) {
  f += someNumber;
}

啊,也许我的困惑在于C数组和std::array之间的区别——基于范围的循环对于后者是有效的,但对于前者则无效,对吧?因为我一直认为并且读到过,C数组不提供关于其自身大小(长度)的信息。 - johnbakers
1
@SebbyJohanns 不,只要C风格数组是静态大小的,它就对两者有效。上面的模板函数向你展示了你可以从这样的数组中获取大小。 - juanchopanza
换句话说,它在堆栈上工作的数组上工作,而不是在堆上对吧? - johnbakers
1
@SebbyJohanns 是的,有点像。它也可以在静态存储中的数组上工作,但那只是一个细节。基本上,当你在“堆”上分配一个数组时,问题在于你所拥有的仅仅是一个指针句柄。大小信息完全丢失了。这是不直接使用堆分配数组的一个强有力的理由。 - juanchopanza

4

使用范围-based for 循环来遍历数组是完全安全的。我猜你担心可能会意外地在指针上使用它:

float* array = new float[100];
for (float& f : array) {
    // ...
}

不过不用担心,编译器会在这种情况下产生一个错误。因此,在不安全的情况下,您将无论如何得到编译错误。

那本来就是他自己愚蠢的错,因为他使用了 new[] - Puppy
1
@DeadMG:“愚蠢”并不等同于“无知”。 - Sebastian Mach
@SebbyJohanns 我理解他的评论就是这个意思。这是一个很好的观点;C数组适用于C语言。而这里是C++环境。使用C++11,你甚至可以在代码中使用std::vector,它可以替代普通的动态数组。所以现在没有理由不使用std::vector了。 - Nikos C.
@SebbyJohanns 只有分析才能给你那些信息。就我个人而言,我从未遇到过使用向量会产生任何实际影响的开销。但当然,我从未编写过需要处理数千个顶点的 GL 应用程序。 - Nikos C.
我是C++的新手,正在学习许多图形教程,所有这些教程都评论了使用复杂对象模型进行顶点常量操作时增加的开销。虽然在这些程序中“vector”用于其他目的,但作者总是更喜欢使用基本结构体和数组来实际存储顶点位置和颜色。我只是遵循这个方法,因为我对此没有足够的知识。 - johnbakers
显示剩余6条评论

2

数组可以作为引用传递,并且类型和长度会被推断出来。

#include <iostream>

template<typename T, size_t N>
void fun(T const (&arr)[N])
{
    for (std::size_t i = 0; i < N; ++i)
       std::cout << arr[i] << " ";
}

int main()
{
   int x[] = { 1, 2, 3 }; // no need to set length here
   fun(x); // 1 2 3, no need to specify type and length here
}

1
在查看此模板时,我不清楚 Nsize_t 如何传递到函数 fun 中 - 它是如何提取 N 的值的? - johnbakers
在模板参数推导期间,编译器会查看参数(x,其类型为int [3])和形式参数(arr,其类型为T [N]),并将int替换为T,将3替换为N,从而得到一个精确匹配。 - TemplateRex
@SebbyJohanns 固定大小数组的类型包含其大小。然后您可以使用模板参数推导来获取它。 - juanchopanza
@SebbyJohanns,指的是类型(编译时),而不是值(运行时)。 - rici
1
@SebbyJohanns 不,大小并没有存储在数组变量中,只是使用模板函数时,编译器可以查找数组类型的定义并推断出大小。 - TemplateRex
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接