c++ - 在基于范围的 for 循环中 & 和 && 有什么区别?

标签 c++ for-loop reference rvalue-reference

我想知道 for (auto& i : v)for (auto&& i : v) 在像这样的基于范围的 for 循环中有什么区别代码:

#include <iostream>
#include <vector>

int main() 
{
    std::vector<int> v = {0, 1, 2, 3, 4, 5};

    std::cout << "Initial values: ";

    for (auto i : v)    // Prints the initial values
        std::cout << i << ' ';
    std::cout << '\n';

    for (auto i : v)    // Doesn't modify v because i is a copy of each value
        std::cout << ++i << ' ';
    std::cout << '\n';

    for (auto& i : v)   // Modifies v because i is a reference
        std::cout << ++i << ' ';
    std::cout << '\n';

    for (auto&& i : v)  // Modifies v because i is a rvalue reference (Am I right?)
        std::cout << ++i << ' ';
    std::cout << '\n';

    for (const auto &i : v) // Wouldn't compile without the /**/ because i is const
        std::cout << /*++*/i << ' ';
    std::cout << '\n';

}

输出:

Initial values: 0 1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6
2 3 4 5 6 7
2 3 4 5 6 7

两者似乎在这里做同样的事情,但我想知道for (auto& i : v)for (auto&& i : v) 在此代码中。

最佳答案

在我提出这个问题 7 年后,我觉得有资格提供一个更完整的答案。

首先我会说我当时选择的代码对于问题的目的来说并不理想。这是因为示例中 &&& 之间没有区别。

事情是这样的:两者

std::vector<int> v = {0, 1, 2, 3, 4, 5};

for (auto& i : v)
{
    std::cout << ++i << ' ';
}

std::cout << '\n';

std::vector<int> v = {0, 1, 2, 3, 4, 5};

for (auto&& i : v)
{
    std::cout << ++i << ' ';
}

std::cout << '\n';

是等价的。

证明如下:

#include <vector>

std::vector<int> v;

void f()
{
    for (auto& i : v)
    {
        static_assert(std::is_same<decltype(i), int&>::value);
    }

    for (auto&& i : v)
    {
        static_assert(std::is_same<decltype(i), int&>::value);
    }
}

但为什么呢?

就像 David G 在评论中所说的那样,由于 reference collapsing,对左值引用的右值引用变成了左值引用。 , 例如

#include <type_traits>
using T1 = int&;
using T2 = T1&&;
static_assert(std::is_same<T1, T2>::value);

但是请注意,这是不同的:

for (int&& i : v)
{
    // ...
}

并且会失败,因为右值引用不能绑定(bind)到左值。引用折叠不适用于这种情况,因为没有类型推导。

TLDR:对于标准容器,&&& 在基于范围的 for 循环中的区别是:

  • value_type& 有效
  • value_type&& 无效
  • auto&auto&& 都等同于value_type&

现在让我们尝试相反的方法:返回右值的可迭代对象。

#include <iostream>

struct Generated
{
    int operator*() const
    {
        return i;
    }

    Generated& operator++()
    {
        ++i;
        return *this;
    }

    bool operator!=(const Generated& x) const
    {
        return i != x.i;
    }

    int i;
};

struct Generator
{
    Generated begin() const { return { 0 }; }
    Generated end() const { return { 6 }; }
};

int main()
{
    Generator g;

    for (const auto& i : g)
    {
        std::cout << /*++*/i << ' ';
    }
    std::cout << '\n';

    for (auto&& i : g)
    {
        std::cout << ++i << ' ';
    }
    std::cout << '\n';
}

在这里,auto& 不起作用,因为您不能将非常量左值绑定(bind)到右值。

现在我们实际上有 const int&int&&:

Generator g;

for (const auto& i : g)
{
    static_assert(std::is_same<decltype(i), const int&>::value);        
}

for (auto&& i : g)
{
    static_assert(std::is_same<decltype(i), int&&>::value); 
}

关于c++ - 在基于范围的 for 循环中 & 和 && 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29320575/

相关文章:

python - 为什么代码运行如此之慢以至于我在其中使用了 for 循环。有没有更快的方法?

c# - .Net 继承 - 自动依赖引用行为问题

c++ - Vector 的最后一个元素在复制和从复制中弹出后发生变化

c++ - 结构与类的性能

c++ - 删除 C++ 中的每个指针作为指向数组的指针是否安全?

javascript - 如何使用 JavaScript 根据复选框值显示/隐藏菜单?

javascript - 通过引用传递对象时出现 IE 问题。和动态添加属性

c++ - 如何确保 boost::optional<T> 对象在发布构建中被初始化?

c++ - 检测断开 Winsock C++

performance - 加快 MATLAB 的傅里叶级数循环