Functional: A Standard C++ Library

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 sort std::sort in 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 an int parameter and returns nothing.
  • std::bind can preset function parameters; here, the parameter of print_num is 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 function multiply, the object m, and fixes the second parameter to 2. std::placeholders::_1 indicates that the first parameter of the new function object will be passed to the first parameter of multiply.

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 in std::function for use in standard library algorithms.

💡 Usage Notes and Tips

  • Performance Considerations: Function objects generally have better optimization potential than function pointers, but std::function incurs 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::bind unnecessary. 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 empty std::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.

Leave a Comment