In the below example, i
has function scope. But it seems that I cannot use i
in the second for loop. Why does for (i : v1)
not work, but for (int i : v1)
works?
在下面的例子中,我有函数范围。但似乎我不能在第二个for循环中使用I。为什么(i: v1)不工作,而(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;
}
4 个解决方案
#1
39
It's a syntactical issue that a range-based for
loop requires a declaration of a named variable, i.e. it requires a type specifier (cf, for example, cppreference.com):
基于范围的for循环需要一个已命名变量的声明,即它需要一个类型说明符(cf,例如cppreference.com):
for ( range_declaration : range_expression ) loop_statement
for (range_declaration: range_expression) loop_statement
range_declaration - a declaration of a named variable, whose type is the type of the element of the sequence represented by range_expression, or a reference to that type. Often uses the auto specifier for automatic type deduction
range_declaration——命名变量的声明,其类型是由range_expression表示的序列元素的类型,或者对该类型的引用。通常使用自动说明自动类型演绎。
Actually I don't know why your question got downvoted; I find your assumption quite OK; just the C++ syntax has decided to define it in another way.
我不知道你的问题为什么被否决了;我觉得你的假设很好;只是c++语法决定用另一种方式定义它。
#2
33
The range-based for
is specifically intended to replace loops akin to the following (this is a somewhat simplistic case; range-based for
, especially the C++17 version, is more general than the example):
基于范围的for是专门用来替换类似于以下的循环的(这是一个稍微简单的例子;基于范围的for,特别是c++ 17版本,比示例更普遍):
for (auto it = range.begin(), end = range.end(); it != end; ++it) {
use(*it);
}
In the majority of cases won't use the values at the different locations but will rather use the element at the location itself:
在大多数情况下,不使用不同位置的值,而是使用位置本身的元素:
- When mutating the elements in the sequence a value doesn't really help.
- 当改变序列中的元素时,一个值实际上并没有帮助。
- In most cases copying the values is expensive and keeping a reference is more effective.
- 在大多数情况下,复制值是昂贵的,保持引用更有效。
- There are even cases where objects can't be copied to start with.
- 甚至在某些情况下,对象一开始就不能被复制。
As a result the designers of range-based for
decided that references absolutely have to be supported. At the same time, it was intended to use a reasonably simplistic rewrite-rule for a range-based for
. The rule which is codified in the standard is this:
因此,基于范围的for设计人员决定绝对必须支持引用。与此同时,它打算对基于范围的函数使用一个相当简单的重写规则。该标准所规定的规则如下:
for (<range-decl>: <range>) { <body> }
is equivalent to
相当于
{
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>
}
}
In particular, the implication is that <range-decl>
is a declaration rather than just naming a variable. The reason for this requirement is that typically the entity used in front of the :
is a reference. However, references cannot be rebound. However, in each iteration of a loop a new reference can be used.
特别是,这意味着
In principle the rewrite rule could work with using assignments if the <range-decl>
isn’t a declaration but rather an lvalue. That would yield its own share of odd behaviors:
原则上,如果
- There would be a difference between
for (T const& x: range)
andT const& x = 0; for (x: range)
: the former works while the latter is an error. - (T const&x: range)与T const&x = 0之间存在差异;for (x: range):前者起作用,而后者是一个错误。
- If the lvalue is a reference to an object located somewhere (
T& x = get_reference(); for (x: range) {...}
) the loop would automatically assign all the values in a range to an object located somewhere. Normally the objects are either located on the stack or in the source range (when the variable is declared as a reference). - 如果lvalue是对位于某个位置的对象的引用(t&x = get_reference();对于(x: range){…})循环将自动地将范围内的所有值分配给位于某个位置的对象。通常,对象要么位于堆栈上,要么位于源范围(当将变量声明为引用时)。
It was consider more reasonable to only allow initialisations than supporting initialisation or assignments depending on how the variable is declared. Looking at the revision history of the proposals (N2930 and predecessors) doesn’t yield a discussion but I vaguely recall that ths point was discussed.
只允许初始化比根据变量声明的方式支持初始化或赋值更合理。查看提案的修订历史(N2930和前任)并没有引起讨论,但我模糊地回想起了这一点。
#3
1
When you are using range-based loops you need a declaration after opening parentheses, not only a variable. The correct syntax is:
当您使用基于范围的循环时,在打开圆括号之后需要一个声明,而不仅仅是一个变量。正确的语法是:
for ( declaration : range ) statement;
You can see this link for more information.
您可以通过这个链接获得更多信息。
In your example: when you declare i
before your while
loop then you can use it in all of the main
function and the scope of it is the main
function. You can use it in that for
body. When you are using the i
variable in your for
range then you aren't declaring it, because you already declared it above, so it will give you an error and it's not correct with C++ syntax.
在您的示例中:当您在while循环之前声明i时,您可以在所有的主函数中使用它,它的范围就是主函数。你可以用在身体上。当你在你的for范围中使用i变量时,你没有声明它,因为你已经在上面声明了它,所以它会给你一个错误,用c++语法它是不正确的。
But when you type int
before the i
in your for
parenthesis then you are declaring another variable with the name of i
, but only for your for
loop and then it is OK with C++ syntax.
但是当你在for圆括号里的i前面输入int时,你就声明了另一个变量,它的名字是i,但是只针对for循环,然后就可以使用c++语法了。
#4
0
The rationale is most likely because this would invoke copy-assignments on the variable, which would become a potential source of great inefficiency and in practice almost never be the intention... if the type supports copy-assignment at all.
So they probably figured it's best to prohibit this.
基本原理很可能是这样的,因为这将调用变量上的复制赋值,这将成为严重低效的潜在来源,在实践中几乎从不是目的……如果类型完全支持复制分配。所以他们可能认为最好禁止这样做。
#1
39
It's a syntactical issue that a range-based for
loop requires a declaration of a named variable, i.e. it requires a type specifier (cf, for example, cppreference.com):
基于范围的for循环需要一个已命名变量的声明,即它需要一个类型说明符(cf,例如cppreference.com):
for ( range_declaration : range_expression ) loop_statement
for (range_declaration: range_expression) loop_statement
range_declaration - a declaration of a named variable, whose type is the type of the element of the sequence represented by range_expression, or a reference to that type. Often uses the auto specifier for automatic type deduction
range_declaration——命名变量的声明,其类型是由range_expression表示的序列元素的类型,或者对该类型的引用。通常使用自动说明自动类型演绎。
Actually I don't know why your question got downvoted; I find your assumption quite OK; just the C++ syntax has decided to define it in another way.
我不知道你的问题为什么被否决了;我觉得你的假设很好;只是c++语法决定用另一种方式定义它。
#2
33
The range-based for
is specifically intended to replace loops akin to the following (this is a somewhat simplistic case; range-based for
, especially the C++17 version, is more general than the example):
基于范围的for是专门用来替换类似于以下的循环的(这是一个稍微简单的例子;基于范围的for,特别是c++ 17版本,比示例更普遍):
for (auto it = range.begin(), end = range.end(); it != end; ++it) {
use(*it);
}
In the majority of cases won't use the values at the different locations but will rather use the element at the location itself:
在大多数情况下,不使用不同位置的值,而是使用位置本身的元素:
- When mutating the elements in the sequence a value doesn't really help.
- 当改变序列中的元素时,一个值实际上并没有帮助。
- In most cases copying the values is expensive and keeping a reference is more effective.
- 在大多数情况下,复制值是昂贵的,保持引用更有效。
- There are even cases where objects can't be copied to start with.
- 甚至在某些情况下,对象一开始就不能被复制。
As a result the designers of range-based for
decided that references absolutely have to be supported. At the same time, it was intended to use a reasonably simplistic rewrite-rule for a range-based for
. The rule which is codified in the standard is this:
因此,基于范围的for设计人员决定绝对必须支持引用。与此同时,它打算对基于范围的函数使用一个相当简单的重写规则。该标准所规定的规则如下:
for (<range-decl>: <range>) { <body> }
is equivalent to
相当于
{
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>
}
}
In particular, the implication is that <range-decl>
is a declaration rather than just naming a variable. The reason for this requirement is that typically the entity used in front of the :
is a reference. However, references cannot be rebound. However, in each iteration of a loop a new reference can be used.
特别是,这意味着
In principle the rewrite rule could work with using assignments if the <range-decl>
isn’t a declaration but rather an lvalue. That would yield its own share of odd behaviors:
原则上,如果
- There would be a difference between
for (T const& x: range)
andT const& x = 0; for (x: range)
: the former works while the latter is an error. - (T const&x: range)与T const&x = 0之间存在差异;for (x: range):前者起作用,而后者是一个错误。
- If the lvalue is a reference to an object located somewhere (
T& x = get_reference(); for (x: range) {...}
) the loop would automatically assign all the values in a range to an object located somewhere. Normally the objects are either located on the stack or in the source range (when the variable is declared as a reference). - 如果lvalue是对位于某个位置的对象的引用(t&x = get_reference();对于(x: range){…})循环将自动地将范围内的所有值分配给位于某个位置的对象。通常,对象要么位于堆栈上,要么位于源范围(当将变量声明为引用时)。
It was consider more reasonable to only allow initialisations than supporting initialisation or assignments depending on how the variable is declared. Looking at the revision history of the proposals (N2930 and predecessors) doesn’t yield a discussion but I vaguely recall that ths point was discussed.
只允许初始化比根据变量声明的方式支持初始化或赋值更合理。查看提案的修订历史(N2930和前任)并没有引起讨论,但我模糊地回想起了这一点。
#3
1
When you are using range-based loops you need a declaration after opening parentheses, not only a variable. The correct syntax is:
当您使用基于范围的循环时,在打开圆括号之后需要一个声明,而不仅仅是一个变量。正确的语法是:
for ( declaration : range ) statement;
You can see this link for more information.
您可以通过这个链接获得更多信息。
In your example: when you declare i
before your while
loop then you can use it in all of the main
function and the scope of it is the main
function. You can use it in that for
body. When you are using the i
variable in your for
range then you aren't declaring it, because you already declared it above, so it will give you an error and it's not correct with C++ syntax.
在您的示例中:当您在while循环之前声明i时,您可以在所有的主函数中使用它,它的范围就是主函数。你可以用在身体上。当你在你的for范围中使用i变量时,你没有声明它,因为你已经在上面声明了它,所以它会给你一个错误,用c++语法它是不正确的。
But when you type int
before the i
in your for
parenthesis then you are declaring another variable with the name of i
, but only for your for
loop and then it is OK with C++ syntax.
但是当你在for圆括号里的i前面输入int时,你就声明了另一个变量,它的名字是i,但是只针对for循环,然后就可以使用c++语法了。
#4
0
The rationale is most likely because this would invoke copy-assignments on the variable, which would become a potential source of great inefficiency and in practice almost never be the intention... if the type supports copy-assignment at all.
So they probably figured it's best to prohibit this.
基本原理很可能是这样的,因为这将调用变量上的复制赋值,这将成为严重低效的潜在来源,在实践中几乎从不是目的……如果类型完全支持复制分配。所以他们可能认为最好禁止这样做。