为啥在这个例子中有“for(auto& x : v)”而不是“for(auto x : &v)”?
Posted
技术标签:
【中文标题】为啥在这个例子中有“for(auto& x : v)”而不是“for(auto x : &v)”?【英文标题】:Why in this example there is "for(auto& x : v)" and not "for(auto x : &v)"?为什么在这个例子中有“for(auto& x : v)”而不是“for(auto x : &v)”? 【发布时间】:2019-08-08 09:42:43 【问题描述】:我在 Bjarne Stroustrup 的 C++ 编程语言(第 4 版)中找到了这段代码。
在这个例子中,就我的理解而言,我们递增 x 而不将 v 的值复制到 x 中。我的问题是,为什么我们引用 x 而不是 v?
我试图通过分解问题并将其写在纸上来理解问题,简化了内存中会发生的事情,但我不明白。
void increment()
int v[]=0,1,2,3,4,5,6,7,8,9;
for(auto& x : v)
++x;
【问题讨论】:
auto& x
将x
声明为对某事物的引用,并且在循环int
数组的上下文中(v
是什么),那么我们让编译器推断出@987654327 @ 是对 int
的引用。如果你写for (int& x : v)
,你会得到同样的结果。
我认为你需要仔细阅读en.cppreference.com/w/cpp/language/range-for。它应该有帮助;)
for (auto &x : v)
表示在每次循环迭代中,x
是对 v
元素的引用。 for (auto x : &v)
将意味着x
是&v
的元素的值,这是没有意义的,因为&v
是v
的地址并且不是容器(即它不代表元素范围) .
还请记住,与许多其他运算符一样,&
可能意味着不同的含义,具体取决于上下文。在例如的上下文中auto& x
(或int& x
)然后它用于声明一个作为引用的变量。在&v
的上下文中,这是创建指向v
(int (*)[10]
类型)的指针的地址运算符,这似乎不是预期的。
【参考方案1】:
这是一个很好的初学者问题。看看 cppreference 在range-based for loops 上有什么。因为v
是数组类型,所以上面的sn-p展开为
auto && __range = v;
for (auto __begin = __range, __end = (__range + bound);
__begin != __end; ++__begin)
auto& x = *__begin;
++x
其中“bound
是数组中元素的数量(如果数组大小未知或类型不完整,则程序格式错误)”[来自上述链接的引用]。
我们可以看到,这种基于范围的 for 循环背后的机制确保我们在此处作用于对 v
的左值引用:auto && __range = v
使用类型推导和转发引用,这样做是正确的。
简而言之,通过循环中的auto& x
部分,您可以控制在取消引用指向范围的迭代器时初始化的内容 (*__begin
)。例如,auto& x
获得对范围内元素的引用(可以更改,影响范围),const auto& x
获得const
限定范围内元素的引用(这你不能变异)。您可以使用auto x
获得每个元素的副本,并使用const auto x
获得每个元素的const
限定副本,但后者几乎没有用处。
【讨论】:
【参考方案2】:因为for
循环正在更改数组元素,所以您需要一个对每个元素的引用以递增它们。
如果你有错误:
for(auto x : v)
++x;
v
中的任何内容都不会改变,因为 x
是数组元素的副本。
制作:
for(auto x : &v)
++x;
无法编译,因为&v
是数组的地址(不是数组中的第一个元素的地址,因为v
是)并且是在范围表达式中无效。
但使用:
for(auto& x : v)
++x;
现在使x
成为v
中元素的引用,因此可以更改它们。
【讨论】:
“只是你通过引用引用了v
”。按地址而非参考。
@Fareanor 正确 - 看到 & 符号并错误地看到了参考!答案已更正。
取V的地址,不会编译出错吗?
@JVApen 正确 - 认为这是经过测试的,但显然没有。编译失败:无效的范围表达式。答案已更正。以上是关于为啥在这个例子中有“for(auto& x : v)”而不是“for(auto x : &v)”?的主要内容,如果未能解决你的问题,请参考以下文章