std::invoke
Uniformly call any callable (function, member pointer, functor, lambda) with std::invoke (C++17), std::invoke_r (C++23), and std::is_invocable traits.
std::invokesince C++17A function template in <functional> that calls any callable object, including free functions, function objects, lambdas, pointers to member functions and pointers to data members, using one uniform call syntax.
Overview
C++ has several kinds of "callable" that use different call syntax: f(args) for functions and functors, (obj.*pmf)(args) or (ptr->*pmf)(args) for member function pointers, and obj.*pmd for data member pointers. Generic code that must accept all of them used to need special cases.
The standard library has long defined an abstract operation called INVOKE that unifies these cases. It was used by std::function, std::bind and std::thread since C++11, but was not directly exposed. Since C++17, std::invoke exposes it. Since C++23, std::invoke_r adds a variant with an explicit return type.
For std::invoke(f, t1, t2, ..., tN), the rules are:
Callable f | First arg t1 | Effect |
|---|---|---|
Member function pointer of class T | T, derived from T, or reference_wrapper<T> | (t1.*f)(t2...) |
| Member function pointer | Pointer-like (raw or smart pointer) | ((*t1).*f)(t2...) |
| Data member pointer (N == 1) | object | t1.*f |
| Data member pointer (N == 1) | pointer-like | (*t1).*f |
| Anything else | n/a | f(t1, ..., tN) |
The return type is std::invoke_result_t<F, Args...>. Value category is preserved: a call returning a reference returns a reference, and arguments are perfectly forwarded.
Since C++17, std::invoke is constexpr conditionally. Since C++20 it is also noexcept when the underlying call is (it was already conditionally noexcept from C++17; check your implementation for earlier modes).
Syntax
#include <functional>
#include <type_traits>
// C++17
template<class F, class... Args>
std::invoke_result_t<F, Args...>
invoke(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_v<F, Args...>);
// C++23: R is the explicit result type; the result is implicitly converted to R
template<class R, class F, class... Args>
constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>);
// Related traits
// C++11: std::result_of (deprecated in C++17, removed in C++20)
// C++17: std::invoke_result, std::is_invocable, std::is_nothrow_invocable
// C++17: std::is_invocable_r, std::is_nothrow_invocable_rstd::invoke_r<void> discards the result. For non-void R, invoke_r avoids a dangling temporary: if R is a reference and the result would bind to a temporary, the program is ill-formed (C++23).
Examples
Uniform dispatch in a generic wrapper
#include <functional>
#include <iostream>
#include <string>
#include <utility>
struct Widget {
int id = 7;
std::string name() const { return "widget-" + std::to_string(id); }
int scaled(int k) const { return id * k; }
};
int twice(int x) { return 2 * x; }
// C++17: works with every callable kind
template <class F, class... Args>
decltype(auto) timed_call(const char* label, F&& f, Args&&... args) {
std::cout << "[call] " << label << '\n';
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...); // C++17
}
int main() {
Widget w;
Widget* pw = &w;
std::cout << timed_call("free fn", twice, 21) << '\n';
std::cout << timed_call("lambda", [](int a, int b) { return a + b; }, 1, 2) << '\n';
std::cout << timed_call("mem fn", &Widget::scaled, w, 3) << '\n';
std::cout << timed_call("via ptr", &Widget::name, pw) << '\n';
std::cout << timed_call("data mem", &Widget::id, w) << '\n';
}Without std::invoke, f(args...) would fail to compile for the member pointer cases.
Projections and member pointers in algorithms
Since C++20, the constrained algorithms in std::ranges call projections through std::invoke, so member pointers work directly:
#include <algorithm>
#include <vector>
#include <string>
struct Employee { std::string name; int salary; };
int main() {
std::vector<Employee> staff{{"Ada", 120}, {"Bob", 90}, {"Cy", 100}};
// C++20: &Employee::salary is invoked as a projection
std::ranges::sort(staff, std::less{}, &Employee::salary);
}The classic std::sort (C++98) and std::for_each do not use INVOKE for their comparator or function arguments, so passing a member pointer to them does not compile. Wrap it with std::mem_fn (C++11), std::bind, or a lambda.
Calling a tuple of arguments
#include <tuple>
#include <functional>
#include <iostream>
int add3(int a, int b, int c) { return a + b + c; }
int main() {
auto args = std::make_tuple(1, 2, 3);
std::cout << std::apply(add3, args) << '\n'; // C++17; std::apply uses INVOKE internally
}Detecting callability (C++17 traits)
#include <type_traits>
#include <string>
struct S { int f(double) const; };
static_assert(std::is_invocable_v<decltype(&S::f), const S&, double>); // C++17
static_assert(!std::is_invocable_v<decltype(&S::f), const S&>); // arity mismatch
static_assert(std::is_invocable_r_v<long, decltype(&S::f), S, double>); // convertible to long
static_assert(std::is_same_v<
std::invoke_result_t<decltype(&S::f), S, double>, int>); // C++17
template <class F, class... A>
auto maybe_call(F&& f, A&&... a) {
if constexpr (std::is_invocable_v<F, A...>) { // C++17
return std::invoke(std::forward<F>(f), std::forward<A>(a)...);
} else {
return 0;
}
}invoke_r (C++23)
#include <functional>
#include <string>
// C++23
void run_callback() {
auto cb = [] { return 42; };
// Discard the result explicitly
std::invoke_r<void>(cb);
// Convert result to long without a separate cast
long v = std::invoke_r<long>(cb);
// Convert to std::string from a const char* result
auto s = std::invoke_r<std::string>([] { return "hello"; });
(void)v; (void)s;
}invoke_r is what std::move_only_function (C++23) and std::function_ref (C++26) rely on conceptually to apply return-type conversions with the correct semantics.
Best Practices
- Use
std::invokein every generic function that accepts a callable. If your template saysf(args...), it rejects member pointers;std::invoke(f, args...)accepts them. - Forward the callable and arguments. Use
std::invoke(std::forward<F>(f), std::forward<Args>(args)...)and returndecltype(auto)so references and value categories are preserved. - Constrain with
std::is_invocable_vor concepts. Since C++20, preferstd::invocable<F, Args...>andstd::regular_invocablefrom<concepts>over hand-written SFINAE. - Propagate
noexcept. Usenoexcept(std::is_nothrow_invocable_v<F, Args...>)on wrapper functions so callers see accurate exception specs. - Replace
std::result_of. It was deprecated in C++17 and removed in C++20. Usestd::invoke_result_t. - Prefer a lambda over
std::bindfor ad-hoc adaptation, and usestd::invokewhen you only need dispatch, not partial application.
Common Pitfalls
- Pre-C++17 availability.
std::invokedoes not exist before C++17. If you must support C++14, write your own helper usingstd::result_ofand overloads onstd::is_member_pointer, or usestd::mem_fnin specific cases. - Overloaded functions and templates.
std::invoke(twice_overloaded, 1)fails because the name does not identify one function. Wrap the call in a lambda or cast to a specific function pointer type. Static member functions andstd::mem_fnhave the same limitation. - Data member pointers take exactly one argument.
std::invoke(&Widget::id, w, 1)is ill-formed. - Lifetime of the object.
std::invoke(&Widget::name, Widget{})is fine when the result is a value, but if the member function returns a reference into the temporary, using it after the full expression dangles. - Reference wrappers.
std::reference_wrapper<T>as the first argument is unwrapped for member pointers. For plain callables,std::reference_wrapper<F>is itself callable and forwards toF, but it will not unwrap other arguments. - Const and ref qualifiers.
std::invoke(&S::f, const_s)fails iffis notconst. Likewise, a&&-qualified member function requires an rvalue object. - Implicit conversions with
invoke_r.invoke_r<R>allows any implicit conversion from the result toR, which can hide narrowing or unwanted construction. PickRdeliberately, and usevoidonly when discarding is the intent. - Compile-time cost. Heavy use of
std::invokein deeply nested templates adds instantiations. If this shows up in build profiles, avoid it in hot header paths where the callable type is known to be a plain functor. constexprsupport.std::invokeisconstexpronly since C++20. In C++17 mode it cannot be used in constant expressions on conforming implementations, even for constexpr callables.
See Also
- std::bind: partial application built on INVOKE (C++11).
- std::any: type-erased value storage (C++17), often paired with callable registries.
std::function(C++11),std::move_only_function(C++23),std::function_ref(C++26): type-erased callable wrappers with INVOKE semantics.std::mem_fn(C++11),std::apply(C++17),std::invoke_result(C++17),std::is_invocable(C++17).