在基于范围的for循环中使用声明的变量

问题描述 投票:52回答:4

在下面的示例中,i具有功能范围。但似乎我不能在第二个for循环中使用i。为什么for (i : v1)不起作用,但for (int i : v1)有效?

#include<iostream>
#include<string>
#include<vector>

int main()
{
    std::vector<int> v1;
    int i;
    while(std::cin>>i)
    {
        v1.push_back(i);
    }

    for(i : v1) //for (int i:v1) works
        std::cout<<i<<"\t";
    cout<<std::endl;
    return 0;
}
c++
4个回答
40
投票

这是一个语法问题,基于范围的for循环需要声明一个命名变量,即它需要一个类型说明符(例如,参见cppreference.com):

for(range_declaration:range_expression)loop_statement

range_declaration - 命名变量的声明,其类型是range_expression表示的序列元素的类型,或对该类型的引用。经常使用auto说明符进行自动类型推导

其实我不知道为什么你的问题被贬低了;我觉得你的假设很好;只是C ++语法决定以另一种方式定义它。


34
投票

基于范围的for专门用于替换类似于以下的循环(这是一个有点简单的情况;基于范围的for,尤其是C ++ 17版本,比示例更通用):

for (auto it = range.begin(), end = range.end(); it != end; ++it) {
   use(*it);
}

在大多数情况下,不会使用不同位置的值,而是使用位置本身的元素:

  1. 当改变序列中的元素时,值实际上没有帮助。
  2. 在大多数情况下,复制值很昂贵,并且保持参考更有效。
  3. 甚至有些情况下无法复制对象。

因此,基于范围的for的设计者决定绝对必须支持参考。与此同时,它的目的是为基于范围的for使用一个相当简单的重写规则。在标准中编纂的规则是:

for (<range-decl>: <range>) { <body> }

相当于

{
    auto&& range = <range>;        // keep the range alive!
    auto   it    = begin(range);   // actually, reality is bit more complicated
    auto   end   = end(range);     // actually, reality is a bit more complicated
    for (; it != end; ++it) {
        <range-decl> = *it;        // this is the rewrite causing your issue
        <body>
    }
}

特别是,暗示<range-decl>是一个声明,而不仅仅是命名变量。这个要求的原因是通常在:前面使用的实体是参考。但是,引用不能反弹。但是,在循环的每次迭代中,可以使用新的引用。

原则上,如果<range-decl>不是声明而是左值,则重写规则可以使用赋值。那会产生一些奇怪的行为:

  • for (T const& x: range)T const& x = 0; for (x: range)之间会有区别:前者有效,后者有误。
  • 如果左值是对位于某处的对象(T& x = get_reference(); for (x: range) {...})的引用,则循环会自动将范围中的所有值分配给位于某处的对象。通常,对象要么位于堆栈上,要么位于源范围内(当变量被声明为引用时)。

根据变量的声明方式,仅允许初始化比支持初始化或赋值更合理。查看提案的修订历史(N2930和前任)并没有引起讨论,但我含糊地回忆起这一点已经讨论过了。


1
投票

当您使用基于范围的循环时,在打开括号后需要声明,而不仅仅是变量。正确的语法是:

 for ( declaration : range ) statement;

你可以看到this link获取更多信息。

在你的例子中:当你在i循环之前声明while然后你可以在所有的main函数中使用它,它的范围是main函数。你可以在那个for身体中使用它。当你在i范围内使用for变量时,你没有声明它,因为你已经在上面声明了它,所以它会给你一个错误,并且它对C ++语法不正确。

但是当您在int括号中的i之前键入for时,您将声明另一个名为i的变量,但仅适用于您的for循环,然后使用C ++语法就可以了。


0
投票

理由很可能是因为这将调用变量上的复制赋值,这将成为效率极低的潜在根源,并且在实践中几乎从不是意图......如果类型完全支持复制赋值。 所以他们可能认为最好禁止这个。

© www.soinside.com 2019 - 2024. All rights reserved.