C++: STL Function Objects

In lesson 37 we learned about iterators.

Now, we'll learn about the "soul" of STL algorithms — function objects.

Algorithms are the skeleton, function objects are the flesh. Only when combined do they unleash the true power of the STL.


1. Function Object Overview

(1) 1.1 What Are Function Objects?

Function objects (Functors) are objects that can be called like functions.

Three types of function objects:

  1. Function pointers
  2. Function object classes (overloading operator())
  3. Lambda expressions (C++11)

(2) 1.2 Why Do We Need Function Objects?

STL algorithms are generic, but specific operations vary by need. Function objects let you customize operations.

Real-life analogy:



2. Function Pointers

(1) 2.1 Basic Usage

Example: Custom sorting with function pointers (Difficulty ⭐⭐)

▶ Example 2: STL Container Usage (Difficulty ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

// Custom comparison function
bool compareDesc(int a, int b) {
 return a > b; // Descending
}

int main() {
 std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
 
 // Use function pointer
 std::sort(v.begin(), v.end(), compareDesc);
 
 for (int x : v) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
3 1 4 1 5 9 2 6

Result:

TEXT 📖 Display only
9 6 5 4 3 2 2 1 1 

💡 Tip:



3. Function Object Classes

(1) 3.1 What Are Function Object Classes?

Function object classes are classes that overload operator(), whose instances can be called like functions.

Example: Custom comparator (Difficulty ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

// Function object class: sort by string length
struct CompareByLength {
 bool operator()(const std::string& a, const std::string& b) const {
 return a.length() < b.length();
 }
};

int main() {
 std::vectorstd::string words = {"apple", "banana", "cat", "dog"};
 
 // Use function object
 std::sort(words.begin(), words.end(), CompareByLength());
 
 for (const auto& w : words) {
 std::cout << w << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

Result:

TEXT 📖 Display only
cat dog apple banana 

(2) 3.2 Advantages of Function Objects

Comparison Function Pointer Function Object Class
State No state Can have state (member variables)
Performance May not be inlined Can be inlined, faster
Flexibility Low High (can be templated)

(3) 3.3 Function Objects with State

Example: Counter function object (Difficulty ⭐⭐⭐)

CPP
#include <iostream>
#include <algorithm>
#include <vector>

// Function object: count elements satisfying a condition
struct Counter {
 int threshold; // Threshold (state)
 
 Counter(int t) : threshold(t) {}
 
 bool operator()(int x) const {
 return x > threshold; // Count elements greater than threshold
 }
};

int main() {
 std::vector<int> v = {1, 5, 10, 15, 20};
 
 // Create function object, set threshold to 10
 Counter counter(10);
 
 // Count elements greater than 10
 int count = std::count_if(v.begin(), v.end(), counter);
 
 std::cout << "Elements greater than 10: " << count << std::endl; // Output: 2
 
 return 0;
}


4. Lambda Expressions

(1) 4.1 What Are Lambda Expressions?

Lambda expressions are anonymous functions introduced in C++11 that can be defined inline wherever a function is needed.

Basic syntax:

CPP
[capture](parameters) -> return_type { body }
Part Description
capture Capture list (capture external variables)
parameters Parameter list
return_type Return type (can be omitted)
body Function body

(2) 4.2 Basic Example

Example: Lambda sorting (Difficulty ⭐)

TEXT 📖 Display only
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
 
 // Sort with Lambda expression (descending)
 std::sort(v.begin(), v.end(), (int a, int b) {
 return a > b;
 });
 
 for (int x : v) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

Output:

TEXT 📖 Display only
3 1 4 1 5 9 2 6

(3) 4.3 Capture List

The capture list determines which external variables a Lambda can access.

Capture Method Description
`` Capture nothing
[x] Capture x by value
[&x] Capture x by reference
[=] Capture all variables by value
[&] Capture all variables by reference
[this] Capture the this pointer (used inside a class)

Example: Lambda with state (Difficulty ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector<int> v = {1, 5, 10, 15, 20};
 int threshold = 10;
 
 // Capture threshold by value
 int count = std::count_if(v.begin(), v.end(),
 [threshold](int x) {
 return x > threshold;
 });
 
 std::cout << "Elements greater than " << threshold << ": " << count << std::endl;
 
 return 0;
}


5. STL Predefined Function Objects

(1) 5.1 Arithmetic Function Objects

The functional header provides common function objects:

Function Object Purpose Example
std::plusT Addition std::plusint()
std::minusT Subtraction std::minusint()
std::multipliesT Multiplication std::multipliesint()
std::dividesT Division std::dividesint()
std::negateT Negation std::negateint()

Example: Doubling with multiplies (Difficulty ⭐)

TEXT 📖 Display only
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 
 // Double all elements
 std::transform(v.begin(), v.end(), v.begin(),
 std::bind(std::multipliesint(), std::placeholders::_1, 2));
 
 for (int x : v) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

(2) 5.2 Comparison Function Objects

Function Object Purpose
std::equal_toT Equal to
std::not_equal_toT Not equal to
std::greaterT Greater than
std::lessT Less than
std::greater_equalT Greater than or equal to
std::less_equalT Less than or equal to

(3) 5.3 Logical Function Objects

Function Object Purpose
std::logical_andT Logical AND
std::logical_orT Logical OR
std::logical_notT Logical NOT


6. Comprehensive Example

▶ Example 1: Score Processor (Difficulty ⭐⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

struct Student {
 std::string name;
 int score;
};

int main() {
 std::vectorStudent students = {
 {"Zhang San", 85},
 {"Li Si", 92},
 {"Wang Wu", 78}
 };
 
 // 1. Sort by score descending
 std::sort(students.begin(), students.end(),
 (const Student& a, const Student& b) {
 return a.score > b.score;
 });
 
 // 2. Find the highest score
 auto max_it = std::max_element(students.begin(), students.end(),
 (const Student& a, const Student& b) {
 return a.score < b.score;
 });
 
 std::cout << "Highest score: " << max_it->name << " " << max_it->score << std::endl;
 
 // 3. Count passing students
 int passed = std::count_if(students.begin(), students.end(),
 (const Student& s) {
 return s.score >= 60;
 });
 
 std::cout << "Students passed: " << passed << std::endl;
 
 return 0;
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
Highest score:  
Students passed:

❓ FAQ

Q: Which is better — Lambda or function object classes? A:- Simple operations → Lambda (concise code) - Complex operations / need reuse → Function object classes (better maintainability)


Q: Can auto deduce a Lambda's type? A: A Lambda's type is a unique anonymous type; it can only be deduced with auto, not written as a concrete type.

TEXT 📖 Display only
auto func = (int x) { return x * 2; };
// std::function<int(int)> func = ... // Also works, but with performance overhead

Q: When should I use std::function? A: Use std::function when you need to store function objects (as member variables, return values, etc.).


▶ Example 3: Lambda Expression (Difficulty ⭐)

CPP
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};

    int sum = 0;
    std::for_each(v.begin(), v.end(), [&sum](int x) {
        sum += x;
    });

    std::cout << "Total: " << sum << std::endl;

    return 0;
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
1 2 3 4 5
💡 Tip: Lambda syntax: [capture](parameters) { body }. [&sum] captures by reference, allowing modification of external variables.


Topic Key Points
Function pointers Simple but limited functionality
Function object classes Customizable operations, can carry state
Lambda Anonymous functions, concise and powerful
Predefined function objects std::plus etc., in functional
Capture list How Lambda accesses external variables

📖 Summary

📝 Exercises

  1. Basic (Difficulty ⭐): Create a functor (a class overloading operator()) that "compares two integers", and test it with std::sort.

  2. Intermediate (Difficulty ⭐⭐): Use std::function to store different types of callable objects (regular functions, lambdas, functors) and call them uniformly.

  3. Challenge (Difficulty ⭐⭐⭐): Use std::bind to bind partial arguments, generating new callable objects. Implement a "argument pre-filling" function adapter.



Next lesson: STL Adapters (#39)

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏