[英]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 tostd::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&
? i
是int
还是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&
. xi
是int
类型的左值,所以我们得到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.