<functional> (C++)
Learn <functional> (C++) step by step with clear examples and exercises.
Title: Mastering C++'s Standard Library Header
Why This Matters
The `` library is an essential part of the C++ Standard Library, offering predefined function objects and adapters that help you write more flexible, reusable, and efficient code. Understanding this library will equip you with powerful tools to tackle complex programming tasks, prepare for interviews, and debug real-world issues in your projects.
This lesson will delve deeper into the `` library, providing examples, best practices, and common mistakes to help you master its use.
Prerequisites
To follow this lesson, you should be familiar with:
- Basic C++ syntax and concepts (variables, functions, loops, etc.)
- Object-oriented programming principles (classes, inheritance, polymorphism)
- Standard Template Library (STL) basics (containers, iterators, algorithms)
- Understanding of function pointers and lambda expressions
Familiarize Yourself with Function Pointers and Lambda Expressions
Before diving into the `` library, it's important to understand function pointers and lambda expressions. Function pointers are variables that store the addresses of functions, while lambda expressions are anonymous functions that can be used like regular functions or stored in variables.
// Function pointer example
int add(int a, int b) { return a + b; }
int (*ptrAdd)(int, int) = &add; // Function pointer to the add function
// Lambda expression example
auto addLambda = [](int a, int b) { return a + b; };
Core Concept
The ` library provides a set of predefined function objects and adapters that help you write more flexible code. These function objects can be used wherever a regular function pointer is expected. C++ provides several predefined function objects in the ` library, which we will explore in detail below.
Function Objects
Function objects are classes that overload the () operator to call a member function when invoked. They can be used wherever a regular function pointer is expected. C++ provides several predefined function objects in the `` library:
std::plus: Adds two values of typeT. Example usage:std::plus()(3, 5)returns8.std::minus: Subtracts the second value from the first. Example usage:std::minus()(3, 5)returns-2.std::multiplies: Multiplies two values of typeT. Example usage:std::multiplies()(3, 5)returns15.std::divides: Divides the first value by the second. Example usage:std::divides()(10, 2)returns5.std::modulus: Calculates the modulus of the first value with respect to the second. Example usage:std::modulus()(7, 3)returns2.std::negate: Negates a value of typeT. Example usage:std::negate()(-5)returns5.std::abs: Returns the absolute value of a value of typeT. Example usage:std::abs()(-5)returns5.std::logical_not: Negates a boolean value. Example usage:std::logical_not()(true)returnsfalse.std::less,std::greater, and their variants (std::less_equal,std::greater_equal,std::not_equal) are comparison function objects that can be used with STL algorithms likestd::sort.
Function Adapters
Function adapters help you create new function objects from existing ones or convert regular functions into callable objects. C++ provides several predefined function adapters in the `` library:
std::ptr_fun(func): Creates a pointer-to-member function object that calls a member function of an object with a specific type and arguments. Example usage:MyClass obj; std::ptr_fun ptrFunc = &MyClass::myMethod; ptrFunc(obj, 5);std::ref(arg): Creates a reference wrapper for an argument, allowing it to be passed by reference to functions that expect lvalue arguments. Example usage:int x = 10; std::function func = [](const int& arg){}; func(std::ref(x));std::bind(func, args...): Binds a function to a specific set of arguments, creating a new callable object that calls the original function with those arguments pre-filled. Example usage:std::function addFive = std::bind(std::plus(), 5); int result = addFive(3);
Worked Example
Let's create a simple program that uses the std::less comparison function object to sort a vector of integers:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
class MyClass {
public:
int myMethod(int arg) { return arg * 2; }
};
int main() {
std::vector<int> numbers = {5, 3, 1, 4, 2};
std::cout << "Unsorted vector:\n";
for (const auto& number : numbers) {
std::cout << number << ' ';
}
std::cout << '\n';
std::sort(numbers.begin(), numbers.end(), std::less<int>());
std::cout << "Sorted vector:\n";
for (const auto& number : numbers) {
std::cout << number << ' ';
}
std::cout << '\n';
std::vector<MyClass> myObjects;
myObjects.push_back(MyClass());
myObjects.push_back(MyClass());
myObjects.push_back(MyClass());
std::sort(myObjects.begin(), myObjects.end(), std::ptr_fun(&MyClass::myMethod));
for (const auto& obj : myObjects) {
std::cout << obj.myMethod(2) << ' ';
}
std::cout << '\n';
return 0;
}
In this example, we first create a vector of integers and print it before sorting the vector using std::less. Then, we create a class MyClass, define a member function myMethod(int arg), and create a vector of MyClass objects. We sort the vector again using std::ptr_fun(&MyClass::myMethod) to sort based on the result of calling myMethod() on each object.
Common Mistakes
- Forgetting to include the `` header: Always remember to include the necessary headers at the beginning of your C++ files:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // Include this for function objects and adapters
- Using function objects with the wrong argument types: Make sure to use function objects that match the types of your arguments. For example, using
std::lesswith a vector ofdoublevalues will result in compilation errors:
std::vector<double> doubles = {5.0, 3.0, 1.0, 4.0, 2.0};
// This line will cause a compile error: std::sort(doubles.begin(), doubles.end(), std::less<int>());
Common Mistakes (Expanded - Function Adapters)
- Using
std::bind()with the wrong number of arguments: Make sure to provide enough arguments when usingstd::bind(). If you bind too few arguments, the remaining arguments will be passed by value, which can lead to unexpected behavior:
auto addFive = std::bind(std::plus<int>(), 5); // This line is incorrect; missing an argument!
int result = addFive(3); // This line will cause a compile error because `addFive` only takes one argument.
- Not capturing the correct arguments when using
std::bind(): When usingstd::bind(), you can capture arguments by position or name. Make sure to capture the correct arguments if you use named capture:
auto addFive = std::bind(std::plus<int>(), std::placeholders::_1, 5); // Capture first argument by position
auto addTenByName = std::bind(std::plus<int>(), std::placeholders::_2, 10); // Capture second argument by name; incorrect!
Practice Questions
- Write a program that uses the
std::plus()function object to calculate the sum of two user-inputted numbers. - Create a class
MyClasswith a member functionmyMethod(int arg). Write a program that sorts a vector ofMyClassobjects using thestd::ptr_funfunction adapter and thestd::sort()algorithm. - Write a program that uses the
std::bind()function adapter to create a new function object that adds 10 to any integer argument. Test this function object with several input arguments. - (Bonus) Create a custom function object that computes the factorial of an integer number using recursion. Use this function object in your programs where you would normally use
std::multiplies. - (Bonus) Write a program that uses the
std::bind()function adapter to create a new function object that calculates the square root of a non-negative integer number using the Newton-Raphson method. Test this function object with several input arguments.
FAQ
- Why use function objects and adapters instead of regular function pointers? Function objects can encapsulate state (data) and behavior (methods), making them more flexible and reusable than simple function pointers. Adapters help you create new function objects from existing ones or convert regular functions into callable objects, further increasing their versatility.
- Can I use function objects with custom classes? Yes! You can define your own function objects by deriving a class from the
std::unary_function,std::binary_function, orstd::functiontemplates provided in the `library, and overloading the()` operator to call a member function. - What is the difference between
std::bind()andstd::ptr_fun<>()? Bothstd::bind()andstd::ptr_fun<>()create function objects from existing functions, but they differ in how they handle member functions:std::ptr_fun<>()creates a pointer-to-member function object, whilestd::bind()allows you to bind arguments to specific values. - Why are function objects useful when using STL algorithms like sort()? Function objects provide a convenient way to customize the behavior of STL algorithms without modifying their source code. By passing a custom comparison function object, you can sort vectors in different orders or even perform complex operations on elements during sorting.
- Why does
std::bind()usestd::placeholders::_1,std::placeholders::_2, etc., instead of the argument names? Thestd::placeholders::_1,std::placeholders::_2, etc., are used to represent arguments when usingstd::bind(). This allows you to create function objects that can be called with any number or order of arguments, regardless of their original names. - Can I use lambda expressions instead of function objects and adapters? Yes! Lambda expressions can often replace the need for function objects and adapters, especially when dealing with simple operations. However, predefined function objects and adapters provide additional functionality and are more efficient in some cases.