使用 lambda 完美转发 pair.second
Perfect forwarding of pair.second using a lambda
为什么此代码段无法编译?
#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))
| ^
-> decltype(auto)
不应该解析为 std::pair<double, char>&
吗?如果我用 -> std::pair<double, char>&
替换 -> decltype(auto)
它会按预期工作。
Shouldn't -> decltype(auto)
evaluate to std::pair<double, char>&
?
没有。这是一个更简单的例子:
struct X {
int i;
};
X x{42};
decltype(auto) i = x.i;
i
是 int
还是 int&
?这是一个 int
。 decltype(auto)
通过将 decltype(...)
应用到 right-hand 端来派生其类型。 decltype(x.i)
只是给你成员的类型,即 int
。
为了获得 int&
你必须做:
decltype(auto) i = (x.i);
因为现在我们得到的类型是decltype((x.i))
,它产生int&
.
decltype
有一个 special rule 用于未加括号的访问 - 所以添加括号可以避免它。这就是 decltype(x.i)
和 decltype((x.i))
可以不同的原因。一旦我们回避了那个,T
类型的左值上的 decltype
会产生 T&
类型。 x.i
是 int
类型的左值,所以我们得到 int&
.
请注意,如果成员 i
的类型为 int&
,那么 decltype(x.i)
和 decltype((x.i))
都将是 int&
.
回到最初的例子,您可以选择将返回的表达式括起来(并删除不必要的 constexpr
):
auto get_second = [](auto&& pair) noexcept -> decltype(auto)
{ return (FWD(pair).second); };
或者只是知道因为我们正在做 class 成员访问,这永远不会是纯右值,所以我们可以简化为使用 auto&&
(不需要额外的括号):
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<1>
,以防您需要其他元素)。
最后,视图命名空间的短名称的典型选择是 rv
(而不是 vw
)。或者只使用 views
.
为什么此代码段无法编译?
#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))
| ^
-> decltype(auto)
不应该解析为 std::pair<double, char>&
吗?如果我用 -> std::pair<double, char>&
替换 -> decltype(auto)
它会按预期工作。
Shouldn't
-> decltype(auto)
evaluate tostd::pair<double, char>&
?
没有。这是一个更简单的例子:
struct X {
int i;
};
X x{42};
decltype(auto) i = x.i;
i
是 int
还是 int&
?这是一个 int
。 decltype(auto)
通过将 decltype(...)
应用到 right-hand 端来派生其类型。 decltype(x.i)
只是给你成员的类型,即 int
。
为了获得 int&
你必须做:
decltype(auto) i = (x.i);
因为现在我们得到的类型是decltype((x.i))
,它产生int&
.
decltype
有一个 special rule 用于未加括号的访问 - 所以添加括号可以避免它。这就是 decltype(x.i)
和 decltype((x.i))
可以不同的原因。一旦我们回避了那个,T
类型的左值上的 decltype
会产生 T&
类型。 x.i
是 int
类型的左值,所以我们得到 int&
.
请注意,如果成员 i
的类型为 int&
,那么 decltype(x.i)
和 decltype((x.i))
都将是 int&
.
回到最初的例子,您可以选择将返回的表达式括起来(并删除不必要的 constexpr
):
auto get_second = [](auto&& pair) noexcept -> decltype(auto)
{ return (FWD(pair).second); };
或者只是知道因为我们正在做 class 成员访问,这永远不会是纯右值,所以我们可以简化为使用 auto&&
(不需要额外的括号):
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<1>
,以防您需要其他元素)。
最后,视图命名空间的短名称的典型选择是 rv
(而不是 vw
)。或者只使用 views
.