C++ Bind VS Lambda Expression

Introduction

I used to use std::bind very often for partially or fully binding a function and pass the bound function to other functions, such as performance profiling. However, recently I spent lots of time only to realize that std::bind cannot be used wherever I want to. In contrast, lambda expressions are more flexible and can be used to replace std::bind.

In this blog post, I would like to discuss std::bind versus lambda expressions in C++.

C++ Bind VS Lambda

In the following example, we could see that std::bind does not seem to be applicable for the functions that have been overloaded. At least I am not aware of any solution that allows std::bind to be used for an overloaded function. In contrast, lambda expressions can work with overloaded functions and mimic the partial binding behaviors that std::bind has.

bind_vs_lambda.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include <functional>
#include <iostream>

class Foo
{
public:
explicit Foo(int num) : m_n{num} {}
void print_n() const { std::cout << m_n << std::endl; }

private:
int m_n;
};

class Bar
{
public:
explicit Bar(int num) : m_n{num} {}
// Two print_n overloaded functions.
void print_n() const { std::cout << m_n << std::endl; }
void print_n(bool line_break) const
{
if (line_break)
{
std::cout << m_n << std::endl;
}
else
{
std::cout << m_n;
}
}

private:
int m_n;
};

class Baz
{
public:
explicit Baz(int num) : m_foo{num} {}
void print_n_bind() const
{
std::function<void()> const foo_print_n{
std::bind(&Foo::print_n, &m_foo)};
foo_print_n();
}
void print_n_lambda() const
{
// This does not work.
// error: capture of non-variable ‘Baz::m_foo’
// Baz::m_foo is not a variable visible to the lambda.
// std::function<void()> const foo_print_n{[&m_foo]()
// { m_foo.print_n(); }};
std::function<void()> const foo_print_n{[this]()
{ this->m_foo.print_n(); }};
foo_print_n();
}

private:
Foo m_foo;
};

class Qux
{
public:
explicit Qux(int num) : m_bar{num} {}
// The same std::bind implementation does not work for Bar::print_n because
// it's overloaded. error: no matching function for call to
// ‘bind(<unresolved overloaded function type>, const Bar*)’
// void print_n_bind() const
// {
// std::function<void()> const bar_print_n{
// std::bind(&Bar::print_n, &m_bar)};
// bar_print_n();
// }
// void print_n_bind(bool line_break) const
// {
// std::function<void(bool)> const bar_print_n{
// std::bind(&Bar::print_n, &m_bar, std::placeholders::_1)};
// bar_print_n(line_break);
// }
void print_n_lambda() const
{
std::function<void()> const foo_print_n{[this]()
{ this->m_bar.print_n(); }};
foo_print_n();
}
void print_n_lambda(bool line_break) const
{
// Mimicking the std::bind implementation.
std::function<void(bool)> const foo_print_n{
[this](bool line_break) { this->m_bar.print_n(line_break); }};
foo_print_n(line_break);
}

private:
Bar m_bar;
};

int main()
{
Foo const foo{0};
Bar const bar{1};
Baz const baz{2};
Qux const qux{3};

// Bind the Foo::print_n to foo.
std::function<void()> const foo_print_n_bind{
std::bind(&Foo::print_n, &foo)};
// Call the member function print_n.
foo_print_n_bind();
// Create a lambda expression that calls foo's member function print_n.
std::function<void()> const foo_print_n_lambda{[&foo]() { foo.print_n(); }};
// Call the lambda expression.
foo_print_n_lambda();

// The same std::bind implementation does not work for Bar::print_n because
// it's overloaded. error: no matching function for call to
// ‘bind(<unresolved overloaded function type>, const Bar*)’
// std::function<void()> const bar_print_n_bind{
// std::bind(&Bar::print_n, &bar)};
// bar_print_n_bind();
// Create a lambda expression that calls bar's member function print_n.
// This still works.
std::function<void()> const bar_print_n_lambda{[&bar]() { bar.print_n(); }};
bar_print_n_lambda();
std::function<void()> const bar_print_n_lambda_line_break{
[&bar]() { bar.print_n(true); }};
bar_print_n_lambda_line_break();

// Use std::bind inside Baz::print_n_bind.
baz.print_n_bind();
// Use lambda inside Baz::print_n_lambda.
baz.print_n_lambda();

// Use lambda inside Qux::print_n_lambda.
qux.print_n_lambda();
// Use lambda inside Qux::print_n_lambda(bool).
qux.print_n_lambda(true);

return 0;
}
1
2
3
4
5
6
7
8
9
10
$ g++ bind_vs_lambda.cpp -o bind_vs_lambda -std=c++14
$ ./bind_vs_lambda
0
0
1
1
2
2
3
3

Conclusions

Instead of using std::bind, probably I should use more lambda expressions for function bindings in the future.

References

Author

Lei Mao

Posted on

09-25-2023

Updated on

09-25-2023

Licensed under


Comments