std::invokeReference (cppreference)- ISO/IEC 14882 C++ Standard Sections:
[func.invoke],[func.require]
std::invoke is a universal, constexpr execution function template in <functional> that invokes any Callable object—including free functions, function pointers, class functors, lambdas, member function pointers (Class::*), and member data pointers—using a single, unified calling interface. Introduced in C++17 and expanded in C++23 with explicit return-type casting (std::invoke_r), std::invoke eliminates syntactic fragmentation across C++ callable categories. It serves as the foundational execution primitive powering generic libraries (std::apply, std::async, std::thread, std::function), C++20 Ranges projections, and the formal C++ INVOKE Protocol Specification.
In C++11 and C++14, invoking different Callable types required completely divergent, mutually incompatible syntax forms:
// Pre-C++17 Incompatible Invocation Syntaxes
auto free_fn = [](int x) { return x * 2; };
free_fn(42); // 1. Direct call syntax: f(args)
struct Widget { int value = 10; int calc(int x) const { return value + x; } };
Widget w;
int (Widget::*mfp)(int) const = &Widget::calc;
(w.*mfp)(42); // 2. Member function pointer requiring object instance: (obj.*mfp)(args)
int Widget::*mdp = &Widget::value;
w.*mdp; // 3. Member data pointer access syntax: obj.*mdp- Generic Template Code Duplication: Writing generic higher-order functions (such as thread pools, task queue wrappers, or event callbacks) required writing complex SFINAE overloads or
std::bindwrappers to handle free functions vs. member function pointers vs. member data pointers separately. - Indirection & Reference Handling Inconsistencies: Invoking a member pointer required checking whether the target object was a value object (
w.*mfp), a raw pointer (ptr->*mfp), or a reference wrapper (ref.get().*mfp).
Solution: ISO C++17 introduced std::invoke(f, args...), standardizing the full C++ INVOKE protocol under a single constexpr function template signature.
When std::invoke(f, t1, t2, ..., tN) is called, the compiler selects one of seven operational branches:
std::invoke(f, t1, t2, ..., tN)
│
┌─────────────────────────────────┼─────────────────────────────────┐
▼ ▼ ▼
1. Member Function Pointer 2. Member Data Pointer 3. Standard Callable
(f is Class::* member method) (f is Class::* member field) (Free function, Lambda, Functor)
│ │ │
(t1.*f)(t2, ..., tN) t1.*f f(t1, t2, ..., tN)
or (t1.get().*f)(...) or t1.get().*f
or ((*t1).*f)(...) or (*t1).*f
-
Member Function Pointer + Object Reference: If
$f$ is a member function pointer of class$C$ , and$t_1$ is an instance of$C$ (or derived):(t1.*f)(t2, ..., tN). -
Member Function Pointer +
reference_wrapper: If$t_1$ is astd::reference_wrapper<C>:(t1.get().*f)(t2, ..., tN). -
Member Function Pointer + Pointer / Smart Pointer: If
$t_1$ is a raw or smart pointer (C*,std::unique_ptr):((*t1).*f)(t2, ..., tN). -
Member Data Pointer + Object Reference: If
$f$ is a member data pointer, returns reference to field:t1.*f. -
Member Data Pointer +
reference_wrapper: Returns field reference:t1.get().*f. -
Member Data Pointer + Pointer / Smart Pointer: Returns field reference:
(*t1).*f. -
Standard Function / Functor / Lambda: In all other cases:
f(t1, t2, ..., tN).
C++23 introduced std::invoke_r<R> (proposal P1030R6). It executes std::invoke(f, args...) and implicitly converts the returned result to the target return type
// C++23 std::invoke_r: Converts return result implicitly to double (or discards if R = void)
double result = std::invoke_r<double>([](int a, int b) { return a + b; }, 10, 20);┌──────────────────────────────────────────────────────────────────┐
│ C++17 │
├──────────────────────────────────────────────────────────────────┤
│ • Introduced std::invoke in <functional> │
│ • Defined formal INVOKE protocol across all callable types │
│ • Added std::is_invocable and std::invoke_result_t traits │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++20 │
├──────────────────────────────────────────────────────────────────┤
│ • std::invoke made fully constexpr-capable │
│ • Introduced C++20 Concepts: std::invocable<F, Args...> │
│ • Underpins C++20 Ranges projection functions │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++23 / C++26 │
├──────────────────────────────────────────────────────────────────┤
│ • Introduced std::invoke_r<R>(f, args...) in C++23 │
│ • Powers modern zero-allocation std::function_ref (C++26) │
└──────────────────────────────────────────────────────────────────┘
Demonstrates a single function template executing heterogeneous callables using std::invoke.
#include <iostream>
#include <functional>
#include <string>
// 1. Free Function
int add_free(int a, int b) { return a + b; }
// 2. Struct with Member Function and Member Data Pointer
struct Calculator {
int factor = 10;
int multiply(int value) const { return value * factor; }
};
int main() {
// 1. Invoke Free Function
int res1 = std::invoke(add_free, 20, 22);
std::cout << "Free Function: " << res1 << " (Expected: 42)\n";
// 2. Invoke Lambda Closure
auto lambda_op = [](int a, int b) { return a - b; };
int res2 = std::invoke(lambda_op, 50, 8);
std::cout << "Lambda: " << res2 << " (Expected: 42)\n";
Calculator calc;
// 3. Invoke Member Function Pointer via Object Reference
int res3 = std::invoke(&Calculator::multiply, calc, 4);
std::cout << "Member Method: " << res3 << " (Expected: 40)\n";
// 4. Invoke Member Function Pointer via Object Pointer
int res4 = std::invoke(&Calculator::multiply, &calc, 5);
std::cout << "Member Pointer: " << res4 << " (Expected: 50)\n";
// 5. Invoke Member Data Pointer (Reads 'factor' field directly!)
int factor_val = std::invoke(&Calculator::factor, calc);
std::cout << "Member Field: " << factor_val << " (Expected: 10)\n";
return 0;
}Demonstrates inspecting return types and constraining generic functions using C++20 concepts.
#include <iostream>
#include <functional>
#include <type_traits>
#include <concepts>
// C++20 Function constrained by std::invocable concept
template <typename F, typename... Args>
requires std::invocable<F, Args...>
constexpr auto execute_and_log(F&& f, Args&&... args) {
std::cout << "Executing constrained callable...\n";
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
int main() {
auto sq = [](int x) constexpr { return x * x; };
// Inspect return type at compile time via std::invoke_result_t
using RetType = std::invoke_result_t<decltype(sq), int>;
static_assert(std::is_same_v<RetType, int>);
constexpr int result = execute_and_log(sq, 7);
std::cout << "Result: " << result << " (Expected: 49)\n";
return 0;
}Demonstrates C++23 std::invoke_r casting returned values to target types implicitly or discarding them cleanly.
#include <iostream>
#include <functional>
int compute_integer() { return 42; }
int main() {
#if defined(__cpp_lib_invoke_r) && __cpp_lib_invoke_r >= 202106L
// C++23 std::invoke_r: Executes function and converts 'int' return to 'double'!
double d_val = std::invoke_r<double>(compute_integer);
// C++23 std::invoke_r with 'void': Discards return value safely!
std::invoke_r<void>(compute_integer);
std::cout << "Converted double result: " << d_val << "\n";
#else
double d_val = static_cast<double>(std::invoke(compute_integer));
std::cout << "Converted double result: " << d_val << "\n";
#endif
return 0;
}-
Always Use
std::invokeinside Generic Wrapper Code:- Inside generic function templates, task queues, or logging wrappers, write
std::invoke(f, std::forward<Args>(args)...)instead of direct function callsf(args...). This ensures your wrapper accepts member pointers (Class::*) and references seamlessly.
- Inside generic function templates, task queues, or logging wrappers, write
-
Member Data Pointer Argument Requirement:
- When passing a member data pointer (
&Class::field) tostd::invoke, you MUST provide an instance ofClass(or pointer/reference wrapper) as the second parameter (std::invoke(&Class::field, object_instance)).
- When passing a member data pointer (
-
Use C++20
std::invocableConcepts:- Replace complex SFINAE traits (
std::enable_if_t<std::is_invocable_v<F, Args...>>) with clean C++20 concept constraints (requires std::invocable<F, Args...>).
- Replace complex SFINAE traits (
- Related to:
- std::apply — Unpacks tuples into
std::invokearguments. - std::function — Polymorphic wrapper using
std::invokeinternally. std::invoke_r(C++23) — Return-type converting variant.- Concepts
std::invocable(C++20) — Type constraints checkingstd::invokevalidity.
- std::apply — Unpacks tuples into
- Superseded by: None.
- Supersedes: Fragmented manual invocation branching for free functions vs. member pointers.
Back to C++17 section.