第21章 函数对象与Lambda表达式

函数对象的深度解析

函数对象(Function Object),也称为仿函数(Functor),是C++中一种特殊的对象,它可以像函数一样被调用。函数对象通过重载operator()运算符实现,使其能够具有函数的行为,同时又可以拥有状态。

函数对象的基本概念

函数对象是一个重载了函数调用运算符()的类的实例。当我们创建这个类的对象后,就可以像调用函数一样使用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Add {
private:
int value;

public:
Add(int v) : value(v) {}

// 函数调用运算符重载
int operator()(int x) const {
return x + value;
}
};

// 使用函数对象
Add add5(5);
int result = add5(10); // 调用operator(),返回15

函数对象的优势

相比普通函数,函数对象具有以下优势:

  1. 可以保持状态:函数对象可以有成员变量,从而在多次调用之间保持状态
  2. 可以被定制:通过模板参数或构造函数参数定制函数对象的行为
  3. 可以内联优化:编译器更容易对函数对象进行内联优化
  4. 可以适配不同的接口:函数对象可以实现不同的接口,如STL中的各种算法要求

函数对象的高级实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 通用的函数对象基类
template <typename Result, typename... Args>
struct FunctionObject {
virtual Result operator()(Args... args) const = 0;
virtual ~FunctionObject() = default;
};

// 具体的函数对象实现
template <typename Result, typename... Args>
class ConcreteFunctionObject : public FunctionObject<Result, Args...> {
private:
Result (*func)(Args...);

public:
explicit ConcreteFunctionObject(Result (*f)(Args...)) : func(f) {}

Result operator()(Args... args) const override {
return func(std::forward<Args>(args)...);
}
};

// 带状态的函数对象
template <typename T>
class Accumulator {
private:
T sum;

public:
Accumulator(T initial = T{}) : sum(initial) {}

// 重载函数调用运算符
T operator()(T value) {
sum += value;
return sum;
}

// 获取当前状态
T getSum() const {
return sum;
}

// 重置状态
void reset(T value = T{}) {
sum = value;
}
};

// 测试带状态的函数对象
void test_accumulator() {
Accumulator<int> acc(10);
std::cout << "After adding 5: " << acc(5) << std::endl; // 15
std::cout << "After adding 3: " << acc(3) << std::endl; // 18
std::cout << "Current sum: " << acc.getSum() << std::endl; // 18
acc.reset();
std::cout << "After reset, adding 7: " << acc(7) << std::endl; // 7
}

函数对象在STL中的应用

STL广泛使用函数对象来实现各种算法的行为定制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 使用标准库中的函数对象
std::vector<int> values = {3, 1, 4, 1, 5, 9, 2, 6};

// 使用std::less进行升序排序
std::sort(values.begin(), values.end(), std::less<int>());

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

// 使用自定义函数对象进行排序
class CustomComparator {
public:
bool operator()(int a, int b) const {
// 按照绝对值大小排序
return std::abs(a) < std::abs(b);
}
};

std::sort(values.begin(), values.end(), CustomComparator());

// 使用函数对象进行查找
class EvenPredicate {
public:
bool operator()(int value) const {
return value % 2 == 0;
}
};

auto it = std::find_if(values.begin(), values.end(), EvenPredicate());
if (it != values.end()) {
std::cout << "First even number: " << *it << std::endl;
}

Lambda表达式的深度解析

Lambda表达式是C++11引入的重要特性,它允许我们在需要函数的地方内联定义一个匿名函数对象。Lambda表达式提供了一种简洁、灵活的方式来创建函数对象。

Lambda表达式的基本语法

1
2
3
[capture clause](parameters) -> return type {
// 函数体
};
  • capture clause:捕获列表,指定哪些外部变量可以在Lambda内部使用,以及如何使用(值捕获或引用捕获)
  • parameters:参数列表,与普通函数的参数列表类似
  • return type:返回类型,通常可以省略,由编译器自动推导
  • function body:函数体,包含Lambda表达式的具体实现

Lambda表达式的捕获方式

Lambda表达式可以通过不同的方式捕获外部变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
int x = 10;
int y = 20;

// 值捕获
auto lambda1 = [x, y]() {
return x + y;
};

// 引用捕获
auto lambda2 = [&x, &y]() {
x++; y++;
return x + y;
};

// 混合捕获
auto lambda3 = [x, &y]() {
y++;
return x + y;
};

// 捕获所有变量(值)
auto lambda4 = [=]() {
return x + y;
};

// 捕获所有变量(引用)
auto lambda5 = [&]() {
x++; y++;
return x + y;
};

// 捕获所有变量(值),但指定某些变量为引用
auto lambda6 = [=, &x]() {
x++;
return x + y;
};

// 空捕获列表
auto lambda7 = []() {
return 42;
};

Lambda表达式的类型

Lambda表达式的类型是由编译器生成的唯一的、未命名的类类型,称为闭包类型(Closure Type)。这个类型重载了operator()运算符,因此Lambda表达式本质上是一个函数对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Lambda表达式的类型推导
auto add = [](int a, int b) {
return a + b;
};

// 使用decltype获取Lambda的类型
decltype(add) add2 = add;
int result = add2(3, 4); // 7

// Lambda作为函数参数
void process(std::function<int(int, int)> func) {
std::cout << func(5, 6) << std::endl;
}

process(add); // 11

// Lambda作为返回值
std::function<int(int)> makeAdder(int x) {
return [x](int y) {
return x + y;
};
}

auto adder = makeAdder(10);
std::cout << adder(5) << std::endl; // 15

泛型Lambda表达式

C++14引入了泛型Lambda表达式,允许我们使用自动类型推导的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 泛型Lambda
auto genericLambda = [](auto a, auto b) {
return a + b;
};

int i = genericLambda(1, 2); // 3
double d = genericLambda(1.5, 2.5); // 4.0
std::string s = genericLambda(std::string("Hello"), std::string(" World")); // "Hello World"

// 带约束的泛型Lambda(C++20)
auto constrainedLambda = []<typename T>(T a, T b) requires std::integral<T> {
return a + b;
};

// 只接受整数类型
int result = constrainedLambda(1, 2); // 3
// constrainedLambda(1.5, 2.5); // 编译错误:double不是整数类型

Lambda表达式的高级应用

1. 递归Lambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 递归Lambda
auto factorial = [](auto self, int n) -> int {
if (n <= 1) return 1;
return n * self(self, n - 1);
};

int result = factorial(factorial, 5); // 120

// 使用std::function包装递归Lambda
std::function<int(int)> factorial2 = [&factorial2](int n) {
if (n <= 1) return 1;
return n * factorial2(n - 1);
};

int result2 = factorial2(5); // 120

2. Lambda表达式与STL算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
std::vector<int> values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 使用Lambda进行排序
std::sort(values.begin(), values.end(), [](int a, int b) {
return a > b; // 降序排序
});

// 使用Lambda进行查找
auto it = std::find_if(values.begin(), values.end(), [](int x) {
return x % 3 == 0;
});

// 使用Lambda进行转换
std::vector<double> doubled;
doubled.reserve(values.size());
std::transform(values.begin(), values.end(), std::back_inserter(doubled), [](int x) {
return x * 2.0;
});

// 使用Lambda进行累积
int sum = std::accumulate(values.begin(), values.end(), 0, [](int acc, int x) {
return acc + x;
});

// 使用Lambda进行过滤
std::vector<int> filtered;
std::copy_if(values.begin(), values.end(), std::back_inserter(filtered), [](int x) {
return x % 2 == 0;
});

3. Lambda表达式与函数适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Lambda作为函数适配器
auto makeAdder = [](int x) {
return [x](int y) {
return x + y;
};
};

auto add5 = makeAdder(5);
auto add10 = makeAdder(10);

std::cout << add5(3) << std::endl; // 8
std::cout << add10(3) << std::endl; // 13

// 组合Lambda表达式
auto compose = [](auto f, auto g) {
return [f, g](auto x) {
return f(g(x));
};
};

auto doubleAndAdd5 = compose(add5, [](int x) { return x * 2; });
std::cout << doubleAndAdd5(10) << std::endl; // 25

函数对象与Lambda的性能比较

性能分析

函数对象和Lambda表达式在性能上各有优势,具体取决于使用场景:

  1. 编译期优化:函数对象和Lambda表达式都可以被编译器内联优化,但Lambda表达式的简洁性可能使编译器更容易进行优化
  2. 运行时开销:两者的运行时开销都很小,接近普通函数
  3. 内存使用:函数对象可能需要存储状态,而Lambda表达式的内存使用取决于捕获的变量
  4. 类型擦除:如果使用std::function包装,会引入类型擦除的开销

性能测试示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <chrono>
#include <iostream>
#include <vector>
#include <algorithm>

// 函数对象
class Multiply {
private:
int factor;

public:
Multiply(int f) : factor(f) {}

int operator()(int x) const {
return x * factor;
}
};

// 普通函数
int multiply(int x, int factor) {
return x * factor;
}

void performance_test() {
const size_t size = 10000000;
std::vector<int> data(size);
std::vector<int> result(size);

// 填充数据
for (size_t i = 0; i < size; ++i) {
data[i] = i % 100;
}

int factor = 5;

// 测试函数对象
Multiply multiplier(factor);
auto start = std::chrono::high_resolution_clock::now();
std::transform(data.begin(), data.end(), result.begin(), multiplier);
auto end = std::chrono::high_resolution_clock::now();
auto duration1 = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Function object: " << duration1 << " ms" << std::endl;

// 测试Lambda表达式
start = std::chrono::high_resolution_clock::now();
std::transform(data.begin(), data.end(), result.begin(), [factor](int x) {
return x * factor;
});
end = std::chrono::high_resolution_clock::now();
auto duration2 = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Lambda expression: " << duration2 << " ms" << std::endl;

// 测试普通函数
start = std::chrono::high_resolution_clock::now();
std::transform(data.begin(), data.end(), result.begin(), [factor](int x) {
return multiply(x, factor);
});
end = std::chrono::high_resolution_clock::now();
auto duration3 = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << "Regular function: " << duration3 << " ms" << std::endl;
}

现代C++中的函数式编程

C++11及以后的标准引入了许多函数式编程的特性,使得C++可以更好地支持函数式编程范式。

函数式编程的核心概念

  1. 不可变数据:尽量使用不可变的数据结构
  2. 纯函数:函数的输出只取决于输入,没有副作用
  3. 函数组合:将多个简单函数组合成复杂函数
  4. 延迟计算:推迟计算直到真正需要结果时

C++中的函数式编程工具

1. std::function

std::function是一个通用的函数包装器,可以包装函数对象、Lambda表达式、函数指针等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <functional>

// 包装函数指针
std::function<int(int, int)> add_func = [](int a, int b) { return a + b; };

// 包装函数对象
Multiply multiplier(5);
std::function<int(int)> multiply_func = multiplier;

// 包装成员函数
class Calculator {
public:
int add(int a, int b) { return a + b; }
};

Calculator calc;
std::function<int(int, int)> member_func = std::bind(&Calculator::add, &calc, std::placeholders::_1, std::placeholders::_2);

2. std::bind

std::bind用于绑定函数的参数,创建一个新的函数对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <functional>

int add(int a, int b, int c) {
return a + b + c;
}

// 绑定前两个参数
auto add5and10 = std::bind(add, 5, 10, std::placeholders::_1);
std::cout << add5and10(20) << std::endl; // 35

// 绑定第三个参数
auto addWith100 = std::bind(add, std::placeholders::_1, std::placeholders::_2, 100);
std::cout << addWith100(10, 20) << std::endl; // 130

3. 函数式算法

STL中的许多算法都支持函数式编程风格:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <algorithm>
#include <vector>
#include <numeric>

std::vector<int> values = {1, 2, 3, 4, 5};

// 映射(transform)
std::vector<int> squared(values.size());
std::transform(values.begin(), values.end(), squared.begin(), [](int x) { return x * x; });

// 过滤(copy_if)
std::vector<int> even_values;
std::copy_if(values.begin(), values.end(), std::back_inserter(even_values), [](int x) { return x % 2 == 0; });

// 折叠(accumulate)
int sum = std::accumulate(values.begin(), values.end(), 0, [](int acc, int x) { return acc + x; });
int product = std::accumulate(values.begin(), values.end(), 1, [](int acc, int x) { return acc * x; });

// 查找(find_if)
auto it = std::find_if(values.begin(), values.end(), [](int x) { return x > 3; });

// 排序(sort)
std::sort(values.begin(), values.end(), [](int a, int b) { return a > b; });

函数对象与Lambda的实际应用

应用场景1:自定义比较器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 自定义比较器(函数对象)
class CaseInsensitiveComparator {
public:
bool operator()(const std::string& a, const std::string& b) const {
return std::lexicographical_compare(
a.begin(), a.end(),
b.begin(), b.end(),
[](char c1, char c2) {
return std::tolower(c1) < std::tolower(c2);
}
);
}
};

// 使用自定义比较器
std::vector<std::string> words = {"Apple", "banana", "Cherry", "date"};
std::sort(words.begin(), words.end(), CaseInsensitiveComparator());

// 使用Lambda作为比较器
std::sort(words.begin(), words.end(), [](const std::string& a, const std::string& b) {
return std::lexicographical_compare(
a.begin(), a.end(),
b.begin(), b.end(),
[](char c1, char c2) {
return std::tolower(c1) < std::tolower(c2);
}
);
});

应用场景2:事件处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 事件处理器
class Event {
public:
using Handler = std::function<void()>;

void add_handler(Handler handler) {
handlers.push_back(std::move(handler));
}

void trigger() {
for (auto& handler : handlers) {
handler();
}
}

private:
std::vector<Handler> handlers;
};

// 使用事件处理器
Event click_event;

// 添加Lambda作为事件处理程序
click_event.add_handler([]() {
std::cout << "Click event triggered!" << std::endl;
});

// 添加带状态的Lambda
int click_count = 0;
click_event.add_handler([&click_count]() {
click_count++;
std::cout << "Click count: " << click_count << std::endl;
});

// 触发事件
click_event.trigger();

应用场景3:策略模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 策略接口
template <typename T>
class SortStrategy {
public:
virtual void sort(std::vector<T>& data) = 0;
virtual ~SortStrategy() = default;
};

// 具体策略(使用函数对象)
template <typename T>
class BubbleSort : public SortStrategy<T> {
private:
class Compare {
public:
bool operator()(const T& a, const T& b) const {
return a > b;
}
};

public:
void sort(std::vector<T>& data) override {
Compare compare;
for (size_t i = 0; i < data.size() - 1; ++i) {
for (size_t j = 0; j < data.size() - i - 1; ++j) {
if (compare(data[j], data[j + 1])) {
std::swap(data[j], data[j + 1]);
}
}
}
}
};

// 排序上下文
template <typename T>
class Sorter {
private:
std::unique_ptr<SortStrategy<T>> strategy;

public:
explicit Sorter(std::unique_ptr<SortStrategy<T>> s) : strategy(std::move(s)) {}

void set_strategy(std::unique_ptr<SortStrategy<T>> s) {
strategy = std::move(s);
}

void sort(std::vector<T>& data) {
strategy->sort(data);
}
};

// 使用策略模式
std::vector<int> values = {5, 2, 9, 1, 5, 6};
Sorter<int> sorter(std::make_unique<BubbleSort<int>>());
sorter.sort(values);

// 使用Lambda作为策略
auto sort_with_lambda = [](std::vector<int>& data, auto comparator) {
std::sort(data.begin(), data.end(), comparator);
};

sort_with_lambda(values, [](int a, int b) { return a < b; });

总结

函数对象和Lambda表达式是C++中强大的编程工具,它们为我们提供了一种灵活、高效的方式来定义和使用函数。本章介绍了:

  1. 函数对象的深度解析:函数对象的基本概念、优势、高级实现和在STL中的应用
  2. Lambda表达式的深度解析:Lambda表达式的语法、捕获方式、类型和高级应用
  3. 函数对象与Lambda的性能比较:编译期优化、运行时开销、内存使用和性能测试
  4. 现代C++中的函数式编程:函数式编程的核心概念和C++中的函数式编程工具
  5. 函数对象与Lambda的实际应用:自定义比较器、事件处理和策略模式

通过合理使用函数对象和Lambda表达式,我们可以编写更加简洁、灵活、高效的C++代码。函数对象适用于需要复用、有状态的场景,而Lambda表达式则适用于一次性使用、简洁表达的场景。在实际开发中,我们应该根据具体需求选择合适的工具,以达到最佳的代码质量和性能。