If you work with the standard library a lot and use the <algorithm> header, you definitely write a lot of functional objects. In C++14, you can use generic lambdas for that. In C++11, you only have non generic lambdas. In the earlier versions of the C++ standard, you can construct functional objects using adapter functions such as bind1st, bind2nd, ptr_fun, mem_fun, mem_fun_ref, or you can write them by hand (because adapter functions look scary). Here is some good news: Boost.Bind can be used instead of ugly adapter functions, and it provides a more human-readable syntax.
Binding and reordering function parameters
Getting ready
A knowledge of standard library functions and algorithms will be helpful.
How to do it...
Let's see some examples of the usage of Boost.Bind along with C++11 lambda classes:
- All the samples require the following headers:
// Contains boost::bind and placeholders.
#include <boost/bind.hpp>
// Utility stuff required by samples.
#include <boost/array.hpp>
#include <algorithm>
#include <functional>
#include <string>
#include <cassert>
- Count values greater than 5 as shown in the following code:
void sample1() {
const boost::array<int, 12> v = {{
1, 2, 3, 4, 5, 6, 7, 100, 99, 98, 97, 96
}};
const std::size_t count0 = std::count_if(v.begin(), v.end(),
[](int x) { return 5 < x; }
);
const std::size_t count1 = std::count_if(v.begin(), v.end(),
boost::bind(std::less<int>(), 5, _1)
);
assert(count0 == count1);
}
- This is how we may count empty strings:
void sample2() {
const boost::array<std::string, 3> v = {{
"We ", "are", " the champions!"
}};
const std::size_t count0 = std::count_if(v.begin(), v.end(),
[](const std::string& s) { return s.empty(); }
);
const std::size_t count1 = std::count_if(v.begin(), v.end(),
boost::bind(&std::string::empty, _1)
);
assert(count0 == count1);
}
- Now, let's count strings with a length less than 5:
void sample3() {
const boost::array<std::string, 3> v = {{
"We ", "are", " the champions!"
}};
const std::size_t count0 = std::count_if(v.begin(), v.end(),
[](const std::string& s) { return s.size() < 5; }
);
const std::size_t count1 = std::count_if(v.begin(), v.end(),
boost::bind(
std::less<std::size_t>(),
boost::bind(&std::string::size, _1),
5
)
);
assert(count0 == count1);
}
- Compare the strings:
void sample4() {
const boost::array<std::string, 3> v = {{
"We ", "are", " the champions!"
}};
std::string s(
"Expensive copy constructor is called when binding"
);
const std::size_t count0 = std::count_if(v.begin(), v.end(),
[&s](const std::string& x) { return x < s; }
);
const std::size_t count1 = std::count_if(v.begin(), v.end(),
boost::bind(std::less<std::string>(), _1, s)
);
assert(count0 == count1);
}
How it works...
The boost::bind function returns a functional object that stores a copy of bound values and a copy of the original functional object. When the actual call to operator() is performed, the stored parameters are passed to the original functional object along with the parameters passed at the time of call.
There's more...
Take a look at the previous examples. When we are binding values, we copy a value into a functional object. For some classes this operation is expensive. Is there a way to bypass copying?
Yes, there is! Boost.Ref library will help us here! It contains two functions, boost::ref() and boost::cref(), the first of which allows us to pass a parameter as a reference, and the second one passes the parameter as a constant reference. The ref() and cref() functions just construct an object of type reference_wrapper<T> or reference_wrapper<const T>, which is implicitly convertible to a reference type. Let's change our last examples:
#include <boost/ref.hpp>
void sample5() {
const boost::array<std::string, 3> v = {{
"We ", "are", " the champions!"
}};
std::string s(
"Expensive copy constructor is NOT called when binding"
);
const std::size_t count1 = std::count_if(v.begin(), v.end(),
boost::bind(std::less<std::string>(), _1, boost::cref(s))
);
// ...
}
You can also reorder, ignore, and duplicate function parameters using bind:
void sample6() {
const auto twice = boost::bind(std::plus<int>(), _1, _1);
assert(twice(2) == 4);
const auto minus_from_second = boost::bind(std::minus<int>(), _2, _1);
assert(minus_from_second(2, 4) == 2);
const auto sum_second_and_third = boost::bind(
std::plus<int>(), _2, _3
);
assert(sum_second_and_third(10, 20, 30) == 50);
}
The functions ref , cref, and bind are accepted to the C++11 standard and are defined in the <functional> header in the std:: namespace. All these functions do not dynamically allocate memory and do not use virtual functions. The objects returned by them are easy to optimize for a good compiler.
Standard library implementations of those functions may have additional optimizations to reduce compilation time or just compiler-specific optimizations. You may use the standard library versions of bind, ref, cref functions with any Boost library or even mix Boost and standard library versions.
If you are using the C++14 compiler, then use generic lambdas instead of std::bind and boost::bind, as they are less obscure and simpler to understand. C++17 lambdas are usable with constexpr, unlike std::bind and boost::bind.
See also
The official documentation contains many more examples and a description of advanced features at http://boost.org/libs/bind.