在 C++20(或更新版本)中完美转发 lambda 捕获

Mar*_*ark 12 c++ lambda perfect-forwarding c++20

在 C++20/C++23 中将参数完美转发到 lambda 捕获的最简洁方法是什么?我的意思是在协程对象内部通过复制捕获右值并通过引用捕获左值:

struct A { int _value{0}; };

auto foo = []<typename T>(T&& a) {
    return [a = std::forward<T>(a)]() mutable {
        ++a._value;
        std::cout << a._value << "\n";
    };
};

A my_a;
auto capture_as_lvalue = foo(my_a);
capture_as_lvalue();              // Prints `1`.
capture_as_lvalue();              // Prints `2`.
capture_as_lvalue();              // Prints `3`.
std::cout << my_a._value << "\n"; // Should print `3`.

auto capture_as_rvalue = foo(A{});
capture_as_rvalue(); // Prints `1`.
Run Code Online (Sandbox Code Playgroud)

这个答案似乎表明上面的内容应该有效,但是上面的程序(https://godbolt.org/z/Mz3caah5o)导致

1
2
3
0 <- should be 3
1
Run Code Online (Sandbox Code Playgroud)

Vittorio Romeo 的一篇博客文章使用宏来达到预期的效果。一个缺点是捕获使用指针语义,而不是引用的隐式语义。在这个答案中, Fabio A. 建议了一种使用演绎指南的更简单的方法:

1
2
3
0 <- should be 3
1
Run Code Online (Sandbox Code Playgroud)

虽然这似乎会产生正确的输出,但这确实会触发地址清理程序(https://godbolt.org/z/6heaxYEhE),而且我不确定这是否是误报。

我的问题:Fabio A. 的建议正确吗?这确实是完美地将变量捕获到 lambda 对象中的最佳方法吗?我理想的解决方案将具有最少的样板,并且还有隐式引用语义而不是指针语义。

康桓瑋*_*康桓瑋 18

用于tuple通过引用或值存储参数,具体取决于它是左值还是右值(您可以使用std::apply扩展可变参数模板版本

auto foo = []<typename T>(T&& a) {
    return [a = std::tuple<T>(std::forward<T>(a))]() mutable {
        ++std::get<0>(a)._value;
        std::cout << std::get<0>(a)._value << "\n";
    };
};
Run Code Online (Sandbox Code Playgroud)

演示