【问题标题】:Perfect forwarding of pair.second using a lambda使用 lambda 完美转发 pair.second
【发布时间】:2020-09-03 19:04:19
【问题描述】:

为什么这个 sn-p 不能编译?

#include <iostream>
#include <vector>
#include <ranges>
#include <unordered_map>

namespace vw = std::ranges::views;

int main()
{
    auto get_second = [](auto&& pair) constexpr noexcept -> decltype(auto)
                      { return std::forward<decltype(pair)>(pair).second; };
    
    std::unordered_map<unsigned, std::pair<double, char> > m = {{5, {0., 'a'}}};
    

    for (auto& [d, c] : m | vw::transform(get_second))
        c = 'b';

    for (auto const& pair : m)
        std::printf("(%u, (%.3f, %c))\n", pair.first, pair.second.first, pair.second.second);
}

错误,使用 gcc 是:

main.cpp: In function 'int main()':
main.cpp:16:53: error: cannot bind non-const lvalue reference of type 'std::pair<double, char>&' to an rvalue of type 'std::__success_type<std::pair<double, char> >::type' {aka 'std::pair<double, char>'}
   16 |     for (auto& [d, c] : m | vw::transform(get_second))
      |                                                     ^

-&gt; decltype(auto) 不应该解析为std::pair&lt;double, char&gt;&amp; 吗?如果我将-&gt; decltype(auto) 替换为-&gt; std::pair&lt;double, char&gt;&amp;,它将按预期工作。

【问题讨论】:

    标签: c++ lambda c++20 perfect-forwarding


    【解决方案1】:

    不应该将-&gt; decltype(auto) 评估为std::pair&lt;double, char&gt;&amp;

    没有。这是一个更简单的例子:

    struct X {
        int i;
    };
    
    X x{42};
    decltype(auto) i = x.i;
    

    iint 还是 int&amp;?这是一个intdecltype(auto) 通过将decltype(...) 应用于右侧来派生其类型。 decltype(x.i) 只是给你成员的类型,即int

    要获得int&amp;,您必须这样做:

    decltype(auto) i = (x.i);
    

    因为现在我们得到的类型是decltype((x.i)),它产生int&amp;

    decltype 有一个 special rule 用于无括号访问 - 所以添加括号可以避开它。这就是decltype(x.i)decltype((x.i)) 可能不同的原因。一旦我们回避那个,decltype 在类型为T 的左值上会产生类型T&amp;x.iint 类型的左值,所以我们得到int&amp;

    注意我说可以不同,而不是必须不同,如果成员iint&amp;类型,那么decltype(x.i)decltype((x.i))应该是int&amp;


    回到原来的例子,您可以选择对返回的表达式加括号(并删除不必要的constexpr):

    auto get_second = [](auto&& pair) noexcept -> decltype(auto)
                      { return (FWD(pair).second); };
    

    或者只是知道因为我们正在进行类成员访问,所以这永远不会是纯右值,所以我们可以简化为使用 auto&amp;&amp;(不需要额外的括号):

    auto get_second = [](auto&& pair) noexcept -> auto&&
                      { return FWD(pair).second; };
    

    标准库本身也提供了以下简称:

    for (auto& [d, c] : m | vw::transform(get_second))
    

    您可以改为:

    for (auto& [d, c] : m | vw::values)
    

    (或elements&lt;1&gt;,以防您需要其他元素)。


    最后,视图命名空间的短名称的典型选择是rv(而不是vw)。或者直接使用views

    【讨论】:

    • 为什么constexpr 什么都不提供? pairconstexpr 构造函数,这意味着 lambda 可以接收 constexpr 类型为 pair 的对象,对吧?
    • 天啊,你和values 一起度过了我的一天。谢谢。
    • @Peregring-lk Lambdas 默认是 constexpr,你不需要写任何东西。
    猜你喜欢
    • 1970-01-01
    • 2017-08-05
    • 2012-11-28
    • 1970-01-01
    • 1970-01-01
    • 2015-10-03
    • 2012-12-20
    • 2014-08-23
    • 1970-01-01
    相关资源
    最近更新 更多