【问题标题】:How does &&, the range based loop and the temporary work together?&&、基于范围的循环和临时循环如何协同工作?
【发布时间】:2018-01-16 12:30:53
【问题描述】:

我看到下面的代码here。描述说:

我们还可以使用 auto&& 捕获作为转发引用。那是, auto&& 将解析为 auto& 用于左值引用,而 auto&& 用于 重估参考。这是一个捕获输出的示例 在临时地图上基于范围的 for 循环。

如果引用的值是左值,我确实理解 auto&&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';
}

【问题讨论】:

  • 为简洁起见,您已经了解reference collapsing。对吗?
  • 仅供参考:这里没有任何进展,只是函数返回中可能存在 RVO
  • @RichardCritten 因为它被标记为 [c++1z],所以没有“潜在的 RVO”。这是暂时的,在 for 循环需要它之前绝对不会实现。在 C++17 之前,它是实现定义的,但在这里它是必需的,不再与 RVO 完全一样。

标签: c++ c++17


【解决方案1】:

在这个例子中auto&amp;&amp; 有两种不同的用法,一种是可见的,一种是隐藏的。为了最大限度地冗长,您的循环扩展为:

{
    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';
    }
}

因此:

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

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


请注意:

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

不太正确。首先,它不被称为“衰减” - 例如,当您将数组传递给函数时会发生衰减 - 它会衰减为指针。此外,auto&amp;&amp; 不会折叠成auto&amp;。只是,如果初始值设定项是左值,auto&amp;&amp;auto&amp; 的行为方式相同。如果初始化器是一个右值,auto&amp;&amp; 可以工作(并产生一个右值引用)而auto&amp; 无法编译。

【讨论】:

  • 您的最后一部分也不太正确。如果您的初始化程序是左值,auto&amp;&amp;auto 推导出为 auto&amp; 并且 确实 执行引用折叠。您可以在this post by Scott Meyers 中查看所有详细信息。
猜你喜欢
  • 2017-11-04
  • 2018-12-28
  • 2022-06-26
  • 1970-01-01
  • 2016-10-31
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多