为啥在这个例子中有“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& xx 声明为对某事物的引用,并且在循环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 的元素的值,这是没有意义的,因为&vv 的地址并且不是容器(即它不代表元素范围) . 还请记住,与许多其他运算符一样,& 可能意味着不同的含义,具体取决于上下文。在例如的上下文中auto& x(或int& x)然后它用于声明一个作为引用的变量。在&v 的上下文中,这是创建指向vint (*)[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)”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 strpos 有用/使用,它在这个例子中有啥好处? [关闭]

为啥 Maven 版本中有 SNAPSHOT? [复制]

为啥在这个例子中 LINQ 更快

在C ++中操作bitset

为啥在这个例子中可以返回临时对象?

为啥在这个例子中需要后期绑定? [复制]