I am trying to invoke the first parameter in a tuple with the second one as a parameter, but can't figure out how to do it.
Example:
#include <tuple>
#include <functional>
struct S {
void operator()(int) {}
};
int main() {
S s;
auto t = std::make_tuple(s, 3);
std::invoke(s, 3);
// std::apply(std::invoke, t); // This is what I want to do
// std::apply(std::invoke<S, int>, t); // I would settle for this "Good Enough"
}
but the code above doesn't compile, and the error is very unhelpful (to me):
error: no matching function for call to '__invoke(void (&)(S&&, int&&), std::__tuple_element_t<0, std::tuple<S, int> >&, std::__tuple_element_t<1, std::tuple<S, int> >&)'
(this is from the "good enough" attempt)
Anyone know how to do this, or if it can't be done?
CodePudding user response:
Wrapping overload/template function in lambda solves lot of issues:
std::apply([](auto&&... args){ std::invoke(args...); }, t);
Possibly with forwarding:
std::invoke(std::forward<decltype(args)>(args)...);
Else as std::invoke is a template function (taking forwarding reference), you need to correct type:
std::apply(std::invoke<S&, int&>, t);
std::apply(std::invoke<S, int>, std::move(t));
About the error reading: std::__tuple_element_t<0, std::tuple<S, int> >& is S&, so error simplify to
error: no matching function for call to
__invoke(void (&)(S&&, int&&), S&, int&)"
You would need void (&)(S&, int&) (or pass S&&, int&& as argument of the function)
CodePudding user response:
std::apply is an option, but you need to use a lambda to invoke f appropriately:
std::apply([](auto f, auto... args) {
std::invoke(f, args...);
}, t);
The reason why std::apply(std::invoke<S, int>, t) doesn't work is that std::invoke is instantiated as std::invoke(S&&, int&&). Since t is an lvalue, it cannot bind to an rvalue reference.
