第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);
|
函数对象的优势
相比普通函数,函数对象具有以下优势:
- 可以保持状态:函数对象可以有成员变量,从而在多次调用之间保持状态
- 可以被定制:通过模板参数或构造函数参数定制函数对象的行为
- 可以内联优化:编译器更容易对函数对象进行内联优化
- 可以适配不同的接口:函数对象可以实现不同的接口,如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; std::cout << "After adding 3: " << acc(3) << std::endl; std::cout << "Current sum: " << acc.getSum() << std::endl; acc.reset(); std::cout << "After reset, adding 7: " << acc(7) << std::endl; }
|
函数对象在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::sort(values.begin(), values.end(), std::less<int>());
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
| auto add = [](int a, int b) { return a + b; };
decltype(add) add2 = add; int result = add2(3, 4);
void process(std::function<int(int, int)> func) { std::cout << func(5, 6) << std::endl; }
process(add);
std::function<int(int)> makeAdder(int x) { return [x](int y) { return x + y; }; }
auto adder = makeAdder(10); std::cout << adder(5) << std::endl;
|
泛型Lambda表达式
C++14引入了泛型Lambda表达式,允许我们使用自动类型推导的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| auto genericLambda = [](auto a, auto b) { return a + b; };
int i = genericLambda(1, 2); double d = genericLambda(1.5, 2.5); std::string s = genericLambda(std::string("Hello"), std::string(" World"));
auto constrainedLambda = []<typename T>(T a, T b) requires std::integral<T> { return a + b; };
int result = constrainedLambda(1, 2);
|
Lambda表达式的高级应用
1. 递归Lambda
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| auto factorial = [](auto self, int n) -> int { if (n <= 1) return 1; return n * self(self, n - 1); };
int result = factorial(factorial, 5);
std::function<int(int)> factorial2 = [&factorial2](int n) { if (n <= 1) return 1; return n * factorial2(n - 1); };
int result2 = factorial2(5);
|
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};
std::sort(values.begin(), values.end(), [](int a, int b) { return a > b; });
auto it = std::find_if(values.begin(), values.end(), [](int x) { return x % 3 == 0; });
std::vector<double> doubled; doubled.reserve(values.size()); std::transform(values.begin(), values.end(), std::back_inserter(doubled), [](int x) { return x * 2.0; });
int sum = std::accumulate(values.begin(), values.end(), 0, [](int acc, int x) { return acc + x; });
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
| 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; std::cout << add10(3) << std::endl;
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;
|
函数对象与Lambda的性能比较
性能分析
函数对象和Lambda表达式在性能上各有优势,具体取决于使用场景:
- 编译期优化:函数对象和Lambda表达式都可以被编译器内联优化,但Lambda表达式的简洁性可能使编译器更容易进行优化
- 运行时开销:两者的运行时开销都很小,接近普通函数
- 内存使用:函数对象可能需要存储状态,而Lambda表达式的内存使用取决于捕获的变量
- 类型擦除:如果使用
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; 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++可以更好地支持函数式编程范式。
函数式编程的核心概念
- 不可变数据:尽量使用不可变的数据结构
- 纯函数:函数的输出只取决于输入,没有副作用
- 函数组合:将多个简单函数组合成复杂函数
- 延迟计算:推迟计算直到真正需要结果时
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;
auto addWith100 = std::bind(add, std::placeholders::_1, std::placeholders::_2, 100); std::cout << addWith100(10, 20) << std::endl;
|
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};
std::vector<int> squared(values.size()); std::transform(values.begin(), values.end(), squared.begin(), [](int x) { return x * x; });
std::vector<int> even_values; std::copy_if(values.begin(), values.end(), std::back_inserter(even_values), [](int x) { return x % 2 == 0; });
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; });
auto it = std::find_if(values.begin(), values.end(), [](int x) { return x > 3; });
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());
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;
click_event.add_handler([]() { std::cout << "Click event triggered!" << std::endl; });
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);
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++中强大的编程工具,它们为我们提供了一种灵活、高效的方式来定义和使用函数。本章介绍了:
- 函数对象的深度解析:函数对象的基本概念、优势、高级实现和在STL中的应用
- Lambda表达式的深度解析:Lambda表达式的语法、捕获方式、类型和高级应用
- 函数对象与Lambda的性能比较:编译期优化、运行时开销、内存使用和性能测试
- 现代C++中的函数式编程:函数式编程的核心概念和C++中的函数式编程工具
- 函数对象与Lambda的实际应用:自定义比较器、事件处理和策略模式
通过合理使用函数对象和Lambda表达式,我们可以编写更加简洁、灵活、高效的C++代码。函数对象适用于需要复用、有状态的场景,而Lambda表达式则适用于一次性使用、简洁表达的场景。在实际开发中,我们应该根据具体需求选择合适的工具,以达到最佳的代码质量和性能。