【问题标题】:range-based for loops in c++c++中基于范围的for循环
【发布时间】:2013-01-04 13:54:03
【问题描述】:

C++11 中可用的“for each”语法风格似乎允许在不知道数组的实际大小(元素数量)的情况下进行数组迭代。我假设,因为它是新标准的一部分,所以这是完全安全的,即使对于 C 数组也是如此。通常,您还必须在操作之前单独知道 C 数组的大小,但我希望任何使用过这种新 C++ 技术的人验证它的工作方式与您期望的完全一样:

extern float bunch[100];

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

关于这种技术的非明显副作用或缺点,我有什么需要了解的吗?它在我看到的代码中没有显示太多,可能是因为大多数代码都是在标准中之前编写的。想确保它的罕见使用不是因为其他一些不为人知的原因。

【问题讨论】:

  • 您始终可以知道数组的大小 (gist.github.com/3959946#file-arrays-h-L33)。如果你把它转换成指针,你就不再知道它的大小了。
  • @R.MartinhoFernandes:要点不错!
  • 一般而言,通过放弃普通数组并迁移到标准库容器,您将过上更快乐的 C++ 生活

标签: c++ arrays templates template-argument-deduction for-range


【解决方案1】:

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

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 获得ti),所以你可以完全避免C 风格的数组:

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

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

【讨论】:

  • 啊,也许我的困惑是C数组和std::array之间的区别——基于范围对后者有效,但对前者无效,对吧?因为我一直认为并读过 C 数组不提供有关其自身大小(长度)的知识
  • @SebbyJohanns 不,它对两者都有效,只要 C 样式的数组是静态大小的。上面的模板函数向您展示了您可以从这样的数组中获取大小。
  • 换句话说,它适用于堆栈上的数组,而不是堆上,对吧?
  • @SebbyJohanns 是的,有点。它也适用于静态存储中的数组,但这只是一个细节。基本上,当您在“堆”上分配一个数组时,问题是您作为句柄所拥有的只是一个指针。尺寸信息完全丢失。这是不直接使用堆分配数组的一个令人信服的理由。
【解决方案2】:

对数组使用基于范围的 for 循环是非常安全的。我想你担心你可能会不小心在指针上使用它:

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

不过不用担心。在这种情况下,编译器会产生错误。因此,在不安全的情况下,无论如何都会出现编译错误。

【讨论】:

  • 无论如何使用new[],这都是他自己的愚蠢错误。
  • @DeadMG:“哑巴”不等于“不知道”。
  • @SebbyJohanns 他指的是std::vector使用连续内存的C++11保证。因此,在使用 C++11 时,无论如何都没有理由使用 new[];您可以随时使用std::vector
  • 换句话说,这是一个数组 vs 向量参数,而不是反对 C 数组动态内存分配的参数
  • @SebbyJohanns 这就是我理解他的评论的方式。这是一个有效的观点; C 数组用于 C。这是 C++。使用 C++11,您甚至可以将 std::vector 与需要纯动态数组的代码一起使用。所以没有理由不再使用std::vector
【解决方案3】:

数组可以作为引用传递,并推导出它们的类型和长度。

#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
}

【讨论】:

  • 在查看此模板时,我不清楚 Nsize_t 是如何传递到函数 fun 的——它是如何提取 N 的值的?
  • 在模板实参推导过程中,编译器查看实参(x 类型为 int[3])和形参(arr 类型为 T[N])并用 int 替换 T和 3 for N 给出完全匹配。
  • @SebbyJohanns 固定大小数组的类型包含大小。然后你使用模板参数推导得到它。
  • 啊,好吧,所以不是用new 创建的数组实际上存储了它自己的大小。这对我来说是个新闻,我认为这是 C++ 功能,而不是 C 功能。
  • @SebbyJohanns 不,大小不存储在数组变量中,只是通过模板函数,编译器可以查找数组类型的定义并推断出大小。
猜你喜欢
  • 1970-01-01
  • 2016-10-31
  • 1970-01-01
  • 2021-04-08
  • 2011-06-23
  • 2014-12-06
相关资源
最近更新 更多