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?
CodePudding user response:
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)...);
};
}