The <functional> header file in the C++ Standard Library provides core support for functional programming styles. It includes a series of tools for creating, composing, and manipulating function objects, allowing us to write more concise and expressive code.
Here are some of the main components of the <functional> header file:
| Category | Main Components | Description |
|---|---|---|
| Function Objects | plus<T>, minus<T>, multiplies<T>, divides<T> |
Arithmetic operation function objects |
equal_to<T>, not_equal_to<T>, greater<T>, less<T> |
Comparison operation function objects | |
logical_and<T>, logical_or<T>, logical_not<T> |
Logical operation function objects | |
| Function Adapters | bind |
Bind parameters to create new callable objects |
mem_fn |
Convert member function pointers to function objects | |
| General Utilities | function |
A general polymorphic function wrapper |
reference_wrapper |
A copyable and assignable reference wrapper | |
hash |
A function object for computing hash values |
🛠️ Main Components and Code Examples
1. Using Standard Function Objects
Standard function objects can be directly used in standard library algorithms, replacing default operations or defining new behaviors.
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 4, 2, 8, 5, 7};
// Sort in descending order using std::greater
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
for (int num : numbers) {
std::cout << num << " "; // Output: 8 7 5 4 2 1
}
std::cout << std::endl;
return 0;
}
- Here, the
std::greater<int>()function object is used as the sorting criterion to sortstd::sortin descending order.
2. Using std::function to Wrap Callable Objects
std::function is a general function wrapper that can store, copy, and call any callable object (regular functions, function objects, lambda expressions, etc.).
#include <iostream>
#include <functional>
void print_num(int i) {
std::cout << i << std::endl;
}
int main() {
// Wrap a regular function
std::function<void(int)> f_display = print_num;
f_display(42); // Output: 42
// Use std::bind to bind parameters
std::function<void()> f_display_31337 = std::bind(print_num, 31337);
f_display_31337(); // Output: 31337
// Wrap a lambda expression
std::function<bool(int)> is_even = [](int n) { return n % 2 == 0; };
std::cout << "Is 4 even? " << is_even(4) << std::endl; // Output: 1 (true)
return 0;
}
std::function<void(int)>represents a function wrapper that takes anintparameter and returns nothing.std::bindcan preset function parameters; here, the parameter ofprint_numis bound to 31337, creating a new callable object with no parameters.
3. Using std::bind to Bind Parameters and Member Functions
std::bind can bind existing callable objects with their parameters to create new callable objects, especially useful for binding member functions.
#include <iostream>
#include <functional>
class Multiplier {
public:
int multiply(int a, int b) {
return a * b;
}
};
int main() {
Multiplier m;
// Bind member function and object, fixing the second parameter to 2
// _1 is a placeholder representing the first parameter of the new function
auto times_two = std::bind(&Multiplier::multiply, &m, std::placeholders::_1, 2);
std::cout << "3 * 2 = " << times_two(3) << std::endl; // Output: 3 * 2 = 6
return 0;
}
std::bind(&Multiplier::multiply, &m, std::placeholders::_1, 2)binds the member functionmultiply, the objectm, and fixes the second parameter to 2.std::placeholders::_1indicates that the first parameter of the new function object will be passed to the first parameter ofmultiply.
4. Combining Lambda Expressions with std::function
Lambda expressions can conveniently define anonymous function objects and are very flexible when used in combination with std::function.
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// Use a lambda expression to define the condition for even numbers
std::function<bool(int)> is_even = [](int n) { return n % 2 == 0; };
// Use std::count_if algorithm to count even numbers
auto count = std::count_if(numbers.begin(), numbers.end(), is_even);
std::cout << "Number of even integers: " << count << std::endl; // Output: 2
return 0;
}
- The lambda expression
[](int n) { return n % 2 == 0; }defines an anonymous function object and is stored instd::functionfor use in standard library algorithms.
💡 Usage Notes and Tips
- Performance Considerations: Function objects generally have better optimization potential than function pointers, but
std::functionincurs some performance overhead due to type erasure. In performance-sensitive code, consider using function objects or lambda expressions directly. - Modern C++ Alternatives: The lambda expressions introduced in C++11 make many uses of
std::bindunnecessary. Typically, lambda expressions are more intuitive and improve code readability. - Empty Wrapper Check: Before using
std::function, you can check if it wraps a callable target (e.g.,if (my_function) {...}) to avoid undefined behavior from calling an emptystd::function.
🔗 Extended Ecosystem
In addition to the standard library, C++ functional programming can also leverage some powerful third-party libraries:
- Boost.Phoenix: Allows functional programming in C++, particularly adept at creating very flexible and higher-order function objects, supporting lambda-style expressions.
- FunctionalPlus: A header-only library inspired by Haskell, providing a series of purely functional operations that help reduce code noise and enhance code expressiveness and maintainability.