如何转发可变的 lambda

How to forward a mutable lambda

这是我尝试编译的代码的简化示例:

#include <iostream>
#include <functional>

template <class F>
auto foo(F&& fun)
{
    return [callback = std::forward<F>(fun)](auto&&... args) {
        std::invoke(callback, std::forward<decltype(args)>(args)...);
    };
}

int main()
{
    std::string cur("running"), target("ok");
    
    foo([s1 = cur, s2 = target](std::string const& arg) /*mutable*/ {
        if (s1 == arg)
        {
            std::cout << s1 << std::endl;
        }
    })("not ok");
    
    return 0;
}

简单地说,我有一个函数 foo 接受可调用对象,并且应该从它们构建一个新的可调用对象。为了这个例子,上面我只是调用 fun 参数,但在实际情况下,对可调用对象进行了一些修饰,并将结果放入一个数据结构中,该数据结构在某些情况下调用此类“动作”条件。

这个例子compiles and works just fine。尝试将可变 lambda 传递给 foo 时出现问题 。当我取消注释上面的 mutable 关键字时,出现此编译错误:

main.cpp: In instantiation of 'foo<main()::<lambda(const string&)> >(main()::<lambda(const string&)>&&)::<lambda(auto:1&& ...)> [with auto:1 = {const char (&)[7]}]':

main.cpp:21:7:   required from here

main.cpp:8:20: error: no matching function for call to 'invoke(const main()::<lambda(const string&)>&, const char [7])'

    8 |         std::invoke(callback, std::forward<decltype(args)>(args)...);

      |         ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In file included from main.cpp:2:

/usr/local/include/c++/11.2.0/functional:94:5: note: candidate: 'template<class _Callable, class ... _Args> std::invoke_result_t<_Callable, _Args ...> std::invoke(_Callable&&, _Args&& ...)'

   94 |     invoke(_Callable&& __fn, _Args&&... __args)

      |     ^~~~~~

/usr/local/include/c++/11.2.0/functional:94:5: note:   template argument deduction/substitution failed:

In file included from /usr/local/include/c++/11.2.0/bits/move.h:57,

                 from /usr/local/include/c++/11.2.0/bits/nested_exception.h:40,

                 from /usr/local/include/c++/11.2.0/exception:148,

                 from /usr/local/include/c++/11.2.0/ios:39,

                 from /usr/local/include/c++/11.2.0/ostream:38,

                 from /usr/local/include/c++/11.2.0/iostream:39,

                 from main.cpp:1:

/usr/local/include/c++/11.2.0/type_traits: In substitution of 'template<class _Fn, class ... _Args> using invoke_result_t = typename std::invoke_result::type [with _Fn = const main()::<lambda(const string&)>&; _Args = {const char (&)[7]}]':

/usr/local/include/c++/11.2.0/functional:94:5:   required by substitution of 'template<class _Callable, class ... _Args> std::invoke_result_t<_Callable, _Args ...> std::invoke(_Callable&&, _Args&& ...) [with _Callable = const main()::<lambda(const string&)>&; _Args = {const char (&)[7]}]'

main.cpp:8:20:   required from 'foo<main()::<lambda(const string&)> >(main()::<lambda(const string&)>&&)::<lambda(auto:1&& ...)> [with auto:1 = {const char (&)[7]}]'

main.cpp:21:7:   required from here

/usr/local/include/c++/11.2.0/type_traits:2933:11: error: no type named 'type' in 'struct std::invoke_result<const main()::<lambda(const string&)>&, const char (&)[7]>'

 2933 |     using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;

      |           ^~~~~~~~~~~~~~~

知道这是为什么吗?我的 foo 也可以接受可变的 lambda 吗?

只需将 mutable 添加到 foo:

中的 lambda
template <class F>
auto foo(F&& fun)
{
    return [callback = std::forward<F>(fun)](auto&&... args) mutable {
                                                             //^^^
        std::invoke(callback, std::forward<decltype(args)>(args)...);
    };
}