std::bind()
, std::bind_front()
, std::bind_back()
and std::placeholders
.Earlier in the chapter, we introduced the concept of partial application. With partial application, we provide only some of a function’s required arguments - the remaining arguments are provided later, elsewhere in our code.
Being able to implement this approach gives us more flexibility in our designs, and is sometimes required. We introduced an example where we wanted to determine if a Player
object was a minimum level but, because of constraints in our design, we were not able to provide the Player
and MinimumLevel
at the same time.
We solved this using a functor. We provided the MinLevel
as a constructor argument, where it then got saved to a member variable. We then later provided the Player
object as an argument to the ()
 operator.
A simplified implementation of the full example we introduced in the Functors lesson is below:
#include <iostream>
struct Player {
int GetLevel() const { return 45; }
};
struct LevelChecker {
int MinLevel {50};
bool operator()(const Player& P) {
return P.GetLevel() >= MinLevel;
}
};
int main() {
Player PlayerOne;
LevelChecker IsLevel40{40};
LevelChecker IsLevel50{50};
if (IsLevel40(PlayerOne)) {
std::cout << "Player is level 40 or above";
}
if (!IsLevel50(PlayerOne)) {
std::cout << "\nBut not level 50 or above";
}
}
Player is level 40 or above
But not level 50 or above
In this lesson, we’ll cover a more direct and flexible way of implementing partial application, using std::bind
.
std::bind()
To support the partial application of a function, we can imagine generating a simple wrapper around that function. When creating the wrapper, we provide some (but not all) of the arguments that the function needs.
The wrapper can overload the ()
operator, thereby creating a function object (a functor) that we can call. When the functor is called, the caller provides the remaining arguments.
The functor then combines the arguments provided at its construction (the "bound" arguments) with the arguments provided to the ()
operator to invoke the underlying function.
Let's imagine we have the following function, that accepts three arguments:
#include <iostream>
void Func(int x) {
std::cout << "x = " << x << '\n';
}
int main() {
Func(1);
}
x = 1
We can use std::bind()
from the <functional>
header to create a functor wrapping this function:
#include <iostream>
#include <functional>
void Func(int x) {
std::cout << "x = " << x << '\n';
}
int main() {
auto Functor{std::bind(Func, 1)};
Functor();
}
x = 1
We can bind multiple arguments by providing them when creating the functor:
#include <iostream>
#include <functional>
void Func(int x, int y, int z) {/*...*/}
int main() {
auto Functor{std::bind(Func, 1, 2, 3)};
Functor();
}
x = 1, y = 2, z = 3
The functor is also reusable - we can call it as many times as needed:
#include <iostream>
#include <functional>
void Func(int x, int y, int z) {/*...*/}
int main() {
auto Functor{std::bind(Func, 1, 2, 3)};
Functor();
Functor();
Functor();
}
x = 1, y = 2, z = 3
x = 1, y = 2, z = 3
x = 1, y = 2, z = 3
Binding all of a function’s arguments is sometimes useful, but more commonly we’ll only want to bind some. This allows other arguments to be provided when we later invoke our functor.
We’ll cover how to do this using std::bind()
later in the lesson, but C++20 and C++23 introduced std::bind_front()
and std::bind_back()
respectively, which are easier to use and capture most use cases.
std::bind_front()
When we want to bind the leftmost arguments in a function’s parameter list and let the later arguments be provided separately, we can use std::bind_front()
.
Below, we bind the x
value of Func()
to 1
, and let the y
and z
be provided later:
#include <iostream>
#include <functional>
void Func(int x, int y, int z) {/*...*/}
int main() {
auto Functor{std::bind_front(Func, 1)};
Functor(2, 3);
Functor(5, 9);
Functor(42, 97);
}
x = 1, y = 2, z = 3
x = 1, y = 5, z = 9
x = 1, y = 42, z = 97
In this example, we bind both x
and y
, requring only z
to be provided later:
#include <iostream>
#include <functional>
void Func(int x, int y, int z) {/*...*/}
int main() {
auto Functor{std::bind_front(Func, 1, 2)};
Functor(3);
Functor(9);
Functor(97);
}
x = 1, y = 2, z = 3
x = 1, y = 2, z = 9
x = 1, y = 2, z = 97
std::bind_back()
Note*: std::bind_back()
is a C++23 feature, and may not yet be available in all compilers. The behavior can be replicated using std::bind
and std::placeholders
in older specifications, which we cover below.*
The std::bind_back()
function works in the opposite way to std::bind_front()
. It lets us bind arguments at the end of our parameter list, leaving the leftmost arguments to be provided later. Below, we bind y
to 2
and z
to 3
, with x
being provided by the caller.
#include <iostream>
#include <functional>
void Func(int x, int y, int z) {/*...*/}
int main() {
auto Functor{std::bind_back(Func, 2, 3)};
Functor(1);
Functor(0);
Functor(-5);
}
x = 1, y = 2, z = 3
x = 0, y = 2, z = 3
x = -5, y = 2, z = 3
std::placeholders
Whilst the std::bind_front()
and std::bind_back()
functions can satisfy most use cases, there are more complex scenarios where we need a bit more flexibility. We can implement these using a combination of std::bind()
and std::placeholders
Within our std::bind()
argument list, we can use identifiers within the std::placeholders
namespace to mark locations where future arguments will go. We have access to std::placeholders::_1
, std::placeholders::_2
, and so on.
When we later call our functor, the first argument we pass will be forwarded to where we placed std::placeholders::_1
, the second will be forwarded to std::placeholders::_2
, and so on.
Below, we reimplement our std::bind_front(Func, 1)
example using these techniques. We bind the first argument of Func()
to the integer 1
, but leave placeholders for the remaining two parameters. We can then provide those when calling the functor:
#include <iostream>
#include <functional>
void Func(int x, int y, int z) {/*...*/}
int main() {
auto Functor{std::bind(Func, 1,
std::placeholders::_1,
std::placeholders::_2
)};
Functor(2, 3);
Functor(5, 9);
Functor(42, 97);
}
x = 1, y = 2, z = 3
x = 1, y = 5, z = 9
x = 1, y = 42, z = 97
In this example, we introduce a using namespace
declaration for std::placeholders
. We also bind the second argument of our underlying function, allowing the first and third to be provided later:
#include <iostream>
#include <functional>
void Func(int x, int y, int z) {/*...*/}
int main() {
using namespace std::placeholders;
auto Functor{std::bind(Func, _1, 100, _2)};
Functor(0, 200);
Functor(200, 0);
}
x = 0, y = 100, z = 200
x = 200, y = 100, z = 0
Our placeholders do not have to be in ascending order. In this example, we don’t bind any arguments. Instead, we position our placeholders such that we create a functor that receives the same arguments as the underlying function, but in a different order:
#include <iostream>
#include <functional>
void Func(int x, int y, int z) {/*...*/}
int main() {
using namespace std::placeholders;
auto Functor{std::bind(Func, _3, _2, _1)};
Functor(1, 2, 3);
}
x = 3, y = 2, z = 1
We can use std::bind()
with member functions. When using this technique, we can treat the object that the member function will be called on as the first argument.
If the member function has any additional arguments, we would also provide those, either as bound values, or std::placeholder
 markers.
Below, we create a functor that, when invoked, behaves like a call to PlayerOne.GetName()
:
#include <iostream>
#include <functional>
struct Player {/*...*/};
int main() {
Player PlayerOne{"Roderick"};
auto GetPlayerName{
std::bind(&Player::GetName, &PlayerOne)};
std::cout << "Player: " << GetPlayerName();
}
Player: Roderick
The object the member function is called upon can also be a placeholder, allowing std::bind()
to act as an alternative to std::mem_fn()
:
#include <iostream>
#include <functional>
struct Player {/*...*/};
int main() {
Player PlayerOne{"Roderick"};
auto GetPlayerName{std::bind(&Player::GetName,
std::placeholders::_1)};
std::cout << "Player: "
<< GetPlayerName(&PlayerOne);
}
Player: Roderick
When binding a member function pointer, std::bind()
allows us to provide the context object either as a value or a pointer, including a smart pointer.
Below, we pass a pointer to our context object. When the functor created by std::bind()
is later invoked, it will visit that pointer to get the current state of our object:
#include <iostream>
#include <functional>
struct Player {/*...*/};
int main() {
Player PlayerOne{"Roderick"};
auto GetPlayerOneName{std::bind(
&Player::GetName,
&PlayerOne // Passing by pointer
)};
std::cout << "Name: "
<< GetPlayerOneName();
PlayerOne.SetName("Anna");
std::cout << "\nName: "
<< GetPlayerOneName();
}
Name: Roderick
Name: Anna
We can alternatively bind by value, copying the context object into the functor created by std::bind()
. When we later invoke the functor, it will use that copy, in the state it was when we bound it:
#include <iostream>
#include <functional>
struct Player {/*...*/};
int main() {
Player PlayerOne{"Roderick"};
auto GetPlayerOneName{std::bind(
&Player::GetName,
PlayerOne // Passing by value
)};
std::cout << "Name: "
<< GetPlayerOneName();
PlayerOne.SetName("Anna");
std::cout << "\nName: "
<< GetPlayerOneName();
}
Name: Roderick
Name: Roderick
std::bind_front()
and std::bind_back()
We can also use std::bind_front()
or std::bind_back()
with member functions where applicable.
The previous examples could be written using std::bind_front()
or std::bind_back()
, removing the need for the explicit placeholder:
#include <iostream>
#include <functional>
struct Player {/*...*/};
int main() {
Player PlayerOne{"Roderick"};
auto GetPlayerName{
std::bind_front(&Player::GetName)};
std::cout << "Player: "
<< GetPlayerName(&PlayerOne);
}
Player: Roderick
In the following example, we use std::bind_front()
to bind a member function that accepts an argument:
#include <iostream>
#include <functional>
struct Player {/*...*/};
int main() {
Player PlayerOne;
auto SetPlayerOneName{std::bind_front(
&Player::SetName, &PlayerOne)};
SetPlayerOneName("Anna");
std::cout << "Player: " << PlayerOne.GetName();
}
Player: Anna
Below, we modify the previous example to use std::bind_back()
, thereby binding the member function’s Name
parameter to "Anna", and accepting the Player
we want to rename as an argument:
#include <iostream>
#include <functional>
struct Player {/*...*/};
int main() {
Player PlayerOne;
auto SetNameToAnna{std::bind_back(
&Player::SetName, "Anna")};
SetNameToAnna(PlayerOne);
std::cout << "Player: " << PlayerOne.GetName();
}
Player: Anna
The previous examples could be implemented using std::bind()
and std::placeholders
if preferred:
auto SetName{std::bind(&Player::SetName,
&PlayerOne, std::placeholders::_1)};
auto SetNameToAnna{std::bind(&Player::SetName,
std::placeholders::_1, "Anna")};
Let’s go back to our Party
code to see a more complex example closer to real problems we’re likely to solve by binding. Let's imagine we want everyone in our Party
to start a Quest
, We already have a StartQuest()
function that comes close to meeting our needs, but not quite:
#include <iostream>
#include <concepts>
#include <functional>
struct Player { std::string Name; };
struct Quest { std::string Name; };
class Party {/*...*/};
void StartQuest(Player& P, Quest& Q) {
std::cout << P.Name << " has started "
<< Q.Name << '\n';
}
int main() {
Party MyParty;
Quest EpicQuest{"The Epic Quest"};
MyParty.for_each(/* ??? */);
}
Rather than writing a custom lambda or functor to integrate MyParty.for_each()
and StartQuest()
, we can instead bridge that gap using binding.
Below, we use std::bind_back()
to create a functor that binds the EpicQuest
parameter. The std::bind_back()
function then returns a functor that accepts a single Player
object as an argument, exactly what MyParty.for_each()
 requires.
Once it receives that argument, it forwards it to StartQuest()
, and provides the EpicQuest
we bound earlier as the second argument:
#include <iostream>
#include <concepts>
#include <functional>
struct Player { std::string Name; };
struct Quest { std::string Name; };
class Party {/*...*/};
void StartQuest(Player&, Quest&) {/*...*/}
int main() {
Party MyParty;
Quest EpicQuest{"The Epic Quest"};
MyParty.for_each(std::bind_back(
StartQuest, EpicQuest
));
}
Anna has started The Epic Quest
Robert has started The Epic Quest
Roderick has started The Epic Quest
Prior to C++23, the previous example can be written using std::bind()
and std::placeholders
:
MyParty.for_each(std::bind(
StartQuest, std::placeholders::_1, EpicQuest
));
In this lesson, we explored the concepts of function binding and partial application and the flexibility they provide when working with functions. The main things we covered include:
Player
and LevelChecker
example.std::bind()
- a generic wrapper for creating functors that wrap some underlying function.std::bind_front()
and std::bind_back()
for easier binding of function arguments in C++20 and C++23.std::placeholders
for marking positions of future arguments in std::bind()
calls.Party
class and a Quest
structure.This lesson covers function binding and partial application using std::bind()
, std::bind_front()
, std::bind_back()
and std::placeholders
.
Comprehensive course covering advanced concepts, and how to use them on large-scale projects.