c++ - 使用 lambda 完美转发pair.second

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

为什么这个代码片段无法编译?

#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>& ?如果我替换 -> decltype(auto)通过-> std::pair<double, char>&它按预期工作。

最佳答案

Shouldn't -> decltype(auto) evaluate to std::pair<double, char>&?

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

struct X {
    int i;
};

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

i一个intint& ?这是一个intdecltype(auto)通过应用 decltype(...) 派生其类型到右侧。 decltype(x.i)只是为您提供成员的类型,即 int .

为了获得int&你必须做:

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

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

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

请注意,我说可以不同,而不是必须不同,如果成员 i类型为int& ,然后都是 decltype(x.i)decltype((x.i))将是int& .


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

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

或者只是知道因为我们正在进行类成员访问,所以这永远不会是纯右值,因此我们可以简化为使用 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> ,以防您需要其他元素)。


最后, View 命名空间短名称的典型选择是 rv (而不是 vw )。或者只是使用 views .

关于c++ - 使用 lambda 完美转发pair.second,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63730124/

相关文章:

python - 是否可以为 Python 中的 lambda 表达式的参数指定默认值?

c++ - 如何制作模板循环?

c++ - 闰秒和 std::chrono

c++ - C++ 新手,Visual Studio Professional 2013 无法运行在 Visual Studios C++ Express 2010 中创建的项目

c++ - 给类成员命名的坏习惯在 STL 中也很常用?

c++ - 从 boost vector 中删除条目

c++ - 运算符重载需要括号

Haskell:where子句引用lambda中的绑定(bind)变量

list - elisp 如何将 lambda 应用于列表?

c++ - 在类里面, `using Base::BaseOfBase;` 应该做什么?