&&、基于范围的循环和临时变量如何一起工作?

dan*_*ani 5 c++ c++17

我在这里看到了下面的代码。描述说:

我们还可以使用 auto&& 捕获作为转发参考。也就是说,对于左值引用,auto&& 将解析为 auto&;对于右值引用,auto&& 将解析为 auto&&。以下是在临时地图上捕获基于范围的 for 循环的输出的示例。

auto&&如果引用的值是左值,我确实理解 to 的auto&衰减(因此在这种情况下没有衰减)。我所努力的是理解临时地图、基于范围的 for 循环和移动的值如何一起工作。您愿意解释一下这些东西如何相互作用,以及为什么可以从正在迭代的临时对象中移动。

#include <iostream>
#include <map>

std::map<std::string, int> get_map()
{
    return {
        { "hello", 1 },
        { "world", 2 },
        { "it's",  3 },
        { "me",    4 },
    };
}

int main() {
    for (auto&& [ k, v ] : get_map())
        std::cout << "k=" << k << " v=" << v << '\n';
}
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 1

auto&&在这个例子中有两种不同的用途,一种是可见的,一种是隐藏的。为了尽可能详细,您的循环扩展到:

{
    auto&& __range = get_map(); // the temporary get_map() is bound to a reference, which 
                                // extends its lifetime to the lifetime of the reference
                                // decltype(__range) is std::map<std::string, int>&&

    auto __begin = __range.begin();
    auto __end = __range.end();     // potential different type from __begin since C++17
    for (; __begin != __end; ++__begin) {
        auto&& __elem = *__begin;  // this is your structured binding declaration
                                   // *__begin is an lvalue, so decltype(__elem) is
                                   // std::pair<std::string const, int> const&


        // the actual destructuring here
        std::string const& k = std::get<0>(__elem);    
        int const& v         = std::get<1>(__elem);

        // now your body
        std::cout << "k=" << k << " v=" << v << '\n';
    }
}
Run Code Online (Sandbox Code Playgroud)

因此:

为什么可以从正在迭代的临时对象中移动。

这段代码中的任何地方都没有发生任何移动。是map在 中就地构建的__range,这就是您要迭代的内容。它超出了最后一个大括号的范围。


请注意:

如果引用的值是左值,我确实理解auto&&to的衰减auto&

不太正确。首先,它不被称为“衰减”——衰减是指当你将一个数组传递给一个函数时发生的情况——它衰减成一个指针。而且,auto&&不会崩溃成auto&. 只是,如果初始化器是左值,auto&&并且auto&行为方式相同。如果初始值设定项是右值,auto&&则可以工作(并生成右值引用),但auto&无法编译。