Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to forward a mutable lambda

Here's a watered down example of the code I'm try to compile:

#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;
}

put simply I have a function foo that accepts callables, and is supposed to build a new callable from them. For the sake of the example, above I'm just invoking the fun argument but in the real case there are some decorations done to the callable and the result is placed into a data structure that invokes such "actions" under some conditions.

This example compiles and works just fine. The problem manifests when trying to pass mutable lambdas to foo. When I uncomment the mutable keyword above, I get this compilation error:

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;

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

Any idea why is that? Can my foo accept mutable lambdas as well?

like image 416
Lorah Attkins Avatar asked Mar 01 '23 10:03

Lorah Attkins


1 Answers

Just add mutable to the lambda inside the foo:

template <class F>
auto foo(F&& fun)
{
    return [callback = std::forward<F>(fun)](auto&&... args) mutable {
                                                             //^^^
        std::invoke(callback, std::forward<decltype(args)>(args)...);
    };
}
like image 185
康桓瑋 Avatar answered Mar 11 '23 09:03

康桓瑋