跳到主要内容

C++ 预定义函数对象

什么是预定义函数对象?

在C++的STL(标准模板库)中,预定义函数对象是指由STL预先定义好的一组函数对象(functors),用于执行各种常见操作,如比较、数学运算和逻辑判断等。它们实际上是重载了函数调用运算符operator()的类,使其实例可以像函数一样被调用。

这些预定义函数对象主要位于<functional>头文件中,为我们提供了一系列可直接使用的功能,无需自己编写相应的函数或函数对象。

提示

预定义函数对象比普通函数更高效,因为它们可以被内联展开,减少函数调用开销。

预定义函数对象的分类

C++预定义函数对象可以分为三大类:

  1. 算术函数对象 - 用于执行基本的算术运算
  2. 关系函数对象 - 用于比较两个值
  3. 逻辑函数对象 - 用于执行逻辑运算

下面我们将详细介绍每种类型。

1. 算术函数对象

算术函数对象用于执行基本的算术运算,包括:

函数对象描述等价表达式
plus<T>加法x + y
minus<T>减法x - y
multiplies<T>乘法x * y
divides<T>除法x / y
modulus<T>取模(求余)x % y
negate<T>取反-x

让我们看一个简单的例子,展示如何使用这些算术函数对象:

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

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

// 使用plus<int>累加vector中的所有元素
std::plus<int> adder;
for (int num : numbers) {
sum = adder(sum, num);
}

std::cout << "Sum: " << sum << std::endl; // 输出: Sum: 15

// 使用multiplies<int>计算vector中所有元素的乘积
int product = 1;
std::multiplies<int> multiplier;
for (int num : numbers) {
product = multiplier(product, num);
}

std::cout << "Product: " << product << std::endl; // 输出: Product: 120

// 使用negate<int>将所有元素取反
std::vector<int> negated;
std::negate<int> negator;
for (int num : numbers) {
negated.push_back(negator(num));
}

std::cout << "Negated: ";
for (int num : negated) {
std::cout << num << " "; // 输出: Negated: -1 -2 -3 -4 -5
}
std::cout << std::endl;

return 0;
}

2. 关系函数对象

关系函数对象用于比较两个值,返回一个布尔值:

函数对象描述等价表达式
equal_to<T>等于x == y
not_equal_to<T>不等于x != y
greater<T>大于x > y
less<T>小于x < y
greater_equal<T>大于等于x >= y
less_equal<T>小于等于x <= y

以下是使用关系函数对象的示例:

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

int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9};

// 使用greater<int>降序排序
std::sort(numbers.begin(), numbers.end(), std::greater<int>());

std::cout << "Descending order: ";
for (int num : numbers) {
std::cout << num << " "; // 输出: Descending order: 9 8 5 2 1
}
std::cout << std::endl;

// 使用less<int>升序排序(这是默认的排序方式)
std::sort(numbers.begin(), numbers.end(), std::less<int>());

std::cout << "Ascending order: ";
for (int num : numbers) {
std::cout << num << " "; // 输出: Ascending order: 1 2 5 8 9
}
std::cout << std::endl;

// 使用equal_to<int>查找特定元素
std::equal_to<int> equals_five;
int count = 0;
for (int num : numbers) {
if (equals_five(num, 5)) {
count++;
}
}

std::cout << "Number of elements equal to 5: " << count << std::endl; // 输出: Number of elements equal to 5: 1

return 0;
}

3. 逻辑函数对象

逻辑函数对象用于执行逻辑运算:

函数对象描述等价表达式
logical_and<T>逻辑与x && y
logical_or<T>逻辑或x || y
logical_not<T>逻辑非!x

下面是逻辑函数对象的使用示例:

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

int main() {
std::vector<bool> values = {true, false, true, false};

// 使用logical_not<bool>取反所有元素
std::vector<bool> negated;
std::transform(values.begin(), values.end(),
std::back_inserter(negated),
std::logical_not<bool>());

std::cout << "Original: ";
for (bool val : values) {
std::cout << (val ? "true " : "false "); // 输出: Original: true false true false
}
std::cout << std::endl;

std::cout << "Negated: ";
for (bool val : negated) {
std::cout << (val ? "true " : "false "); // 输出: Negated: false true false true
}
std::cout << std::endl;

// 使用logical_and<bool>检查是否所有元素都为true
bool all_true = true;
std::logical_and<bool> logical_and;
for (bool val : values) {
all_true = logical_and(all_true, val);
}

std::cout << "All true? " << (all_true ? "Yes" : "No") << std::endl; // 输出: All true? No

// 使用logical_or<bool>检查是否至少有一个元素为true
bool any_true = false;
std::logical_or<bool> logical_or;
for (bool val : values) {
any_true = logical_or(any_true, val);
}

std::cout << "Any true? " << (any_true ? "Yes" : "No") << std::endl; // 输出: Any true? Yes

return 0;
}

在STL算法中应用预定义函数对象

预定义函数对象最常见的应用场景是在STL算法中作为谓词(predicate)或比较器(comparator)。

排序与查找

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

int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};

// 使用greater<int>进行降序排序
std::sort(numbers.begin(), numbers.end(), std::greater<int>());

std::cout << "Sorted (descending): ";
for (int num : numbers) {
std::cout << num << " "; // 输出: 9 8 7 6 5 4 3 2 1
}
std::cout << std::endl;

// 查找第一个大于5的元素
auto it = std::find_if(numbers.begin(), numbers.end(),
std::bind(std::greater<int>(), std::placeholders::_1, 5));

if (it != numbers.end()) {
std::cout << "First element greater than 5: " << *it << std::endl; // 输出: First element greater than 5: 9
}

return 0;
}

统计与筛选

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

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 计算大于5的元素数量
int count = std::count_if(numbers.begin(), numbers.end(),
std::bind(std::greater<int>(), std::placeholders::_1, 5));

std::cout << "Number of elements greater than 5: " << count << std::endl; // 输出: Number of elements greater than 5: 5

// 移除所有小于5的元素
numbers.erase(
std::remove_if(numbers.begin(), numbers.end(),
std::bind(std::less<int>(), std::placeholders::_1, 5)),
numbers.end()
);

std::cout << "After removing elements less than 5: ";
for (int num : numbers) {
std::cout << num << " "; // 输出: After removing elements less than 5: 5 6 7 8 9 10
}
std::cout << std::endl;

return 0;
}

实际应用案例

案例1:自定义数据结构排序

假设我们有一个表示学生的结构体,需要根据成绩对学生进行排序:

cpp
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <string>

// 学生结构体
struct Student {
std::string name;
int score;

Student(const std::string& n, int s) : name(n), score(s) {}
};

int main() {
std::vector<Student> students = {
{"Alice", 85},
{"Bob", 92},
{"Charlie", 78},
{"David", 90},
{"Eve", 95}
};

// 使用greater<int>按分数降序排序(高分在前)
std::sort(students.begin(), students.end(),
[](const Student& a, const Student& b) {
return std::greater<int>()(a.score, b.score);
});

std::cout << "Students sorted by score (high to low):" << std::endl;
for (const auto& student : students) {
std::cout << student.name << ": " << student.score << std::endl;
}

return 0;
}

输出结果:

Students sorted by score (high to low):
Eve: 95
Bob: 92
David: 90
Alice: 85
Charlie: 78

案例2:自定义优先队列

使用预定义函数对象创建一个最小堆的优先队列:

cpp
#include <iostream>
#include <functional>
#include <queue>
#include <vector>

int main() {
// 默认的优先队列是最大堆
std::priority_queue<int> max_heap;

// 使用less<int>创建一个最小堆
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;

// 添加相同的元素到两个堆
for (int num : {3, 1, 4, 1, 5, 9, 2, 6}) {
max_heap.push(num);
min_heap.push(num);
}

std::cout << "Max heap elements (popped in order): ";
while (!max_heap.empty()) {
std::cout << max_heap.top() << " "; // 输出: 9 6 5 4 3 2 1 1
max_heap.pop();
}
std::cout << std::endl;

std::cout << "Min heap elements (popped in order): ";
while (!min_heap.empty()) {
std::cout << min_heap.top() << " "; // 输出: 1 1 2 3 4 5 6 9
min_heap.pop();
}
std::cout << std::endl;

return 0;
}

案例3:函数式编程风格的数据处理

使用预定义函数对象实现函数式编程风格的数据处理:

cpp
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <numeric>

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

// 使用plus<int>和accumulate计算总和
int sum = std::accumulate(numbers.begin(), numbers.end(), 0, std::plus<int>());
std::cout << "Sum: " << sum << std::endl; // 输出: Sum: 15

// 使用multiplies<int>和accumulate计算乘积
int product = std::accumulate(numbers.begin(), numbers.end(), 1, std::multiplies<int>());
std::cout << "Product: " << product << std::endl; // 输出: Product: 120

// 使用transform和negate<int>创建一个新的容器,其中包含所有元素的相反数
std::vector<int> negated(numbers.size());
std::transform(numbers.begin(), numbers.end(), negated.begin(), std::negate<int>());

std::cout << "Negated: ";
for (int num : negated) {
std::cout << num << " "; // 输出: Negated: -1 -2 -3 -4 -5
}
std::cout << std::endl;

return 0;
}

C++ 11的无模板参数函数对象

从C++11开始,STL允许我们在使用预定义函数对象时省略模板参数,编译器可以根据上下文推导出类型:

cpp
// C++11之前
std::sort(v.begin(), v.end(), std::greater<int>());

// C++11及以后
std::sort(v.begin(), v.end(), std::greater<>()); // 编译器自动推导类型

这使代码更加简洁,特别是在处理复杂类型时。

总结

C++预定义函数对象是STL提供的强大工具,能让我们以简洁的方式执行常见操作:

  1. 算术函数对象 (plus<>, minus<>, multiplies<> 等) 用于执行基本数学运算
  2. 关系函数对象 (greater<>, less<>, equal_to<> 等) 用于比较值
  3. 逻辑函数对象 (logical_and<>, logical_or<>, logical_not<>) 用于逻辑操作

这些函数对象的主要优势包括:

  • 高效性 - 它们可以被内联展开,减少函数调用开销
  • 可复用性 - 可以在不同的STL算法中重复使用
  • 类型安全 - 编译时类型检查确保操作的安全性
  • 灵活性 - 可以作为参数传递给其他函数或算法

预定义函数对象在STL算法、容器排序和数据处理中特别有用,是现代C++编程不可或缺的部分。

练习

  1. 使用std::transformstd::multiplies<>将一个整数向量中的所有元素乘以2。
  2. 创建一个程序,使用std::sortstd::greater<>按照字符串长度降序排序一个字符串向量。
  3. 实现一个函数,使用std::count_if和适当的函数对象,计算一个整数向量中偶数的数量。
  4. 使用std::accumulatestd::plus<>计算一个包含浮点数的向量的平均值。
  5. 创建一个自定义结构体,并使用预定义函数对象作为比较器,实现按照多个字段排序。

进一步学习资源

通过充分理解和掌握预定义函数对象,你将能够编写更加简洁、高效和可维护的C++代码。