Language[lang]
C++ function pointer: syntax, callbacks and examples
- Section
- Language [lang]
- Standard
- C++17
- Compilers
- MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
- Published
- Length
- 7 min read
In this answer8 clauses
- 1How do you declare, assign and call a function pointer?
- 2How do you make function pointer syntax readable?
- 3How do you pass a function to another function?
- 4What is an array of function pointers?
- 5Can a lambda be a function pointer?
- 6Function pointer, std::function or a template: which should you use?
- 7How do you point to a member function?
- 8A function pointer cheat sheet
Abstract
A C++ function pointer is a variable that holds the address of a function: after int (*op)(int, int) = add;, the call op(2, 3) runs add. Name the type once with using Operation = int (*)(int, int); to pass functions as callbacks or keep them in an array. It holds plain functions, static member functions and lambdas with an empty capture list; a capturing lambda needs std::function or a template.

A C++ function pointer is a variable that holds the address of a function, so a program can choose at run time which function to call. int (*op)(int, int) = add; declares one named op for any function that takes two ints and returns an int; op(2, 3) then calls the function it points to.
The smallest whole program aims op at two functions in turn:
#include <iostream>
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int main() {
int (*op)(int, int) = add;
std::cout << op(2, 3) << '\n'; // 5
op = subtract;
std::cout << op(2, 3) << '\n'; // -1
}— end example]
It printed 5, then -1: the same call, op(2, 3), ran add and then subtract.
The samples are C++17, built with MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2 and Clang 23.1 (libc++), and every call printed the same result on all four. Since When lists the standard and compiler releases each C++ feature needs.
How do you declare, assign and call a function pointer?
Write the return type, then (*name), then the parameter types in parentheses: a function declaration with (*op) where the function’s name would be. Read it from the name outwards: (*op) says op is a pointer, (int, int) lists the parameters of what it points to, and int is what that returns. The parentheses around *op are required:
int (*op)(int, int) = &add; // the same as = add;
int* wrong(int, int); // no parentheses: a function
static_assert(std::is_pointer_v<decltype(op)>);
static_assert(std::is_function_v<decltype(wrong)>);
std::cout << std::boolalpha << (add == &add) << '\n'; // true
std::cout << op(2, 3) << ' ' << (*op)(2, 3) << '\n'; // 5 5— end example]
Without them, int* wrong(int, int); declares a function that returns int*. add == &add printed true: a function’s name converts to its address by itself (the function-to-pointer conversion, [conv.func]), so = add and = &add store the same thing. Calling needs no * either: op(2, 3) and (*op)(2, 3) both printed 5.

How do you make function pointer syntax readable?
Name the type once with using (or the older typedef), then declare variables and parameters with that name like any other type:
using Operation = int (*)(int, int);
typedef int (*OldOperation)(int, int); // the older spelling
static_assert(std::is_same_v<Operation, OldOperation>);
void show(Operation op) {
std::cout << op(6, 3) << '\n';
}
int main() {
Operation op = add;
show(op); // 9
show(multiply); // 18
}— end example]
It printed 9 and 18. The static_assert confirms that the two spellings name the same type; in the typedef, the new name sits where a variable’s name would. For a variable, auto op = add; deduces the same type, int (*)(int, int); parameters and arrays still need it spelled out.
How do you pass a function to another function?
Give the receiving function a parameter of function pointer type. It calls back whichever function it was given, which is why a function passed this way is called a callback:
int apply(Operation op, int a, int b) {
return op(a, b); // calls back the function it was given
}
// ...
std::cout << apply(add, 6, 3) << ' '
<< apply(subtract, 6, 3) << ' '
<< apply(multiply, 6, 3) << '\n'; // 9 3 18— end example]
It printed 9 3 18. The standard library takes callbacks too: given the plain function bool shorter(const std::, the call std:: put subtract, add, divide and plus in the order add, plus, divide, subtract. Sorting with std::sort covers custom comparisons.
What is an array of function pointers?
An array whose elements are function pointers of one type, so an index picks the function to call: a dispatch table in place of a switch.
const Operation table[] = {add, subtract, multiply};
const char* const names[] = {"add", "subtract", "multiply"};
for (std::size_t i = 0; i < std::size(table); ++i) {
std::cout << names[i] << ' ' << table[i](6, 3) << '\n';
}
std::size_t choice = 2;
std::cout << table[choice](7, 6) << '\n'; // multiply: 42— end example]
It printed add 9, subtract 3 and multiply 18, then 42: index 2 picked multiply.
Can a lambda be a function pointer?
Yes, when its capture list is empty. A lambda (an unnamed function written where it is used) with an empty capture list, [], converts to a function pointer by itself (lambda expressions):
Operation op = [](int a, int b) { return a * b; };
std::cout << op(6, 3) << '\n'; // 18— end example]
It printed 18. A lambda that captures a local variable, here keeping its own copy of bonus, does not convert:
int bonus = 10;
Operation op = [bonus](int a, int b) { return a + b + bonus; };— end example]
All four compilers rejected the second line. GCC 16.2 put it most plainly: error: cannot convert 'main()::. No message names the capture as the cause. The cause: a capturing lambda carries its captured values with it, and a plain function pointer has nowhere to keep them.
Function pointer, std::function or a template: which should you use?
Choose by what has to be accepted: a function pointer takes only plain functions, static member functions and captureless lambdas, while std::function and a template parameter take any callable. The same job, written three ways:
int run_pointer(Operation op, int a, int b) {
return op(a, b);
}
int run_function(const std::function<int(int, int)>& op,
int a, int b) {
return op(a, b);
}
template<class F>
int run_template(F op, int a, int b) {
return op(a, b);
}— end example]
All three ran add and printed 9. The capturing lambda of the previous section went through run_function and run_template (19 from each); it would not convert to the Operation that run_pointer takes.
| Way | Accepts | Header | Use it for |
|---|---|---|---|
| Function pointer | plain functions, static member functions, captureless lambdas | none | plain functions and C interfaces |
std::function |
any copyable callable with that signature, capturing lambdas included | <functional> |
storing any callable |
| Template parameter | any callable, its type fixed when compiled | none | taking a callable as an argument, as std::sort does |
A function pointer was the size of a pointer in every build; a std::function was larger on all three standard libraries, by a different amount on each. What std::function costs and what to use instead goes further.
How do you point to a member function?
With a pointer to member function: a plain function pointer holds only non-member and static member functions (C++23 adds explicit object member functions). The member kind puts the class name before the *, takes its address with &Counter::add, and needs an object to call it on, through .*. Since C++17, std::invoke from <functional> makes the same call without that syntax. Here Counter::add(int n) adds n to a running total and returns it, and Counter::sum is static:
Counter counter;
int (Counter::*method)(int) = &Counter::add;
std::cout << (counter.*method)(5) << '\n'; // 5
std::cout << std::invoke(method, counter, 5) << '\n'; // 10
// ...
Operation op = &Counter::sum; // static: a plain pointer will do
std::cout << op(6, 3) << '\n'; // 9— end example]
It printed 5, 10 and 9: std::invoke called add on the same counter, whose total went from 5 to 10, and the static sum went into a plain Operation.
A function pointer cheat sheet
| Task | Code |
|---|---|
| Declare and assign | int (*op)(int, int) = add; |
| Re-aim | op = subtract; |
| Call | op(2, 3) or (*op)(2, 3) |
| Name the type | using Operation = int (*)(int, int); |
| Pass as a parameter | int apply(Operation op, int a, int b) |
| Array | const Operation table[] = {add, subtract, multiply}; |
| Check for null | if (op) |
| Point to a member function | int (Counter:: |
| Call the member function | std:: |
More answers about the core language are in Language.
Questions
How do you typedef a function pointer?
Write typedef int (*Operation)(int, int);, with the new name where the pointer’s name would go. The alias declaration using Operation = int (*)(int, int); names the same type with the name first, which reads more easily. Either way, Operation then declares variables and parameters like any other type.
What is the difference between a function pointer and std::function?
A function pointer holds only plain functions, static member functions and lambdas with an empty capture list, and needs no header. A std::function, from <functional>, holds any copyable callable with its signature, capturing lambdas included, and is larger than a function pointer on Microsoft's standard library, libstdc++ and libc++.
Can a function pointer point to a member function?
Up to C++20, only to a static one, which goes into a plain function pointer like any other function. A non-static member function needs a pointer to member function, such as int (Counter::*method)(int) = &Counter::add;, and an object to call it on: (counter.*method)(5), or std::invoke(method, counter, 5) since C++17.
What is a callback function?
A function you pass to other code for that code to call. In C++ the receiver can take it as a function pointer parameter, such as op in int apply(int (*op)(int, int), int a, int b), and call it with op(a, b). std::sort accepts a plain comparison function too.
Can you take the address of an overloaded function?
Yes: the pointer’s type picks the overload. With add(int, int) and add(double, double) both declared, int (*int_add)(int, int) = add; takes the int version and double (*double_add)(double, double) = add; the double one. If no overload matches the pointer’s type, the line is ill-formed: it does not compile.
Sources
- Pointer declarationcppreference.com
- [conv.func] Function-to-pointer conversionC++ standard working draft
- Lambda expressionscppreference.com
- std::functioncppreference.com
- std::invoke, std::invoke_rcppreference.com
- Address of an overloaded functioncppreference.com