简体   繁体   English

使用 lambda 完美转发 pair.second

[英]Perfect forwarding of pair.second using a lambda

Why this snippet doesn't compile?为什么这个片段不能编译?

#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);
}

The error, using gcc is:使用 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))
      |                                                     ^

Shouldn't -> decltype(auto) resolve to std::pair<double, char>& ?不应该-> decltype(auto)解析为std::pair<double, char>&吗? If I replace -> decltype(auto) by -> std::pair<double, char>& it works as expected.如果我将-> decltype(auto)替换为-> std::pair<double, char>&它会按预期工作。

Shouldn't -> decltype(auto) evaluate to std::pair<double, char>& ?不应该-> decltype(auto)评估为std::pair<double, char>&吗?

No. Here's a much simpler example:不。这是一个更简单的例子:

struct X {
    int i;
};

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

Is i an int or an int& ? iint还是int& It's an int .这是一个int decltype(auto) derives its type by applying decltype(...) to the right-hand side. decltype(auto)通过将decltype(...)应用于右侧来派生其类型。 decltype(xi) just gives you the type of the member, that's int . decltype(xi)只为您提供成员的类型,即int

In order to get an int& you have to do:为了获得int&你必须这样做:

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

Because now we get the type as decltype((xi)) , which yields int& .因为现在我们得到类型为decltype((xi)) ,它产生int&

decltype has a special rule for unparenthesized access - so adding parentheses sidesteps it. decltype对无括号访问有一个特殊的规则——所以添加括号可以避开它。 This is why decltype(xi) and decltype((xi)) can differ.这就是decltype(xi)decltype((xi))可以不同的原因。 Once we sidestep that one, decltype on an lvalue of type T yields the type T& .一旦我们回避了那个,在T类型的左值上的decltype产生类型T& xi is an lvalue of type int , so we get int& . xiint类型的左值,所以我们得到int&

Note that I said can differ and not must differ, if the member i were of type int& , then both decltype(xi) and decltype((xi)) would be int& .请注意,如果成员i的类型为int& ,则我说可以不同而不是必须不同,那么decltype(xi)decltype((xi))都是int&


Going back to the original example, you have the choice of doing either parenthesizing the returned expression (and dropping the unnecessary constexpr ):回到原始示例,您可以选择将返回的表达式括起来(并删除不必要的constexpr ):

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

Or just knowing that because we're doing class member access, this will never be a prvalue, so we can simplify to using auto&& (without the need for additional parentheses):或者只是知道因为我们在做类成员访问,这永远不会是一个纯右值,所以我们可以简化为使用auto&& (不需要额外的括号):

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

Also the standard library itself comes with shorthands for this:标准库本身也为此提供了简写:

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

You can instead write:你可以写:

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

(or also elements<1> , in case you need other elements). (或者也是elements<1> ,以防您需要其他元素)。


Lastly, the typical choice for a short name for the views namespace is rv (rather than vw ).最后,视图命名空间的短名称的典型选择是rv (而不是vw )。 Or just use views .或者只是使用views

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM