C++ Lambda 表达式

目录

  1. #1-lambda-表达式基本概念
  2. #2-lambda-表达式完整语法
  3. #3-捕获列表详解
  4. #4-参数列表与可变规范
  5. #5-返回类型与异常规范
  6. #6-lambda-与-stl-算法
  7. #7-高级特性与技巧
  8. #8-性能考虑与最佳实践

1. Lambda 表达式基本概念

1.1 什么是 Lambda 表达式?

Lambda 表达式是 C++11 引入的匿名函数对象,允许在代码中内联定义函数。

基本语法

参数列表 -> 返回类型 { 函数体 }

1.2 最简单的 Lambda 表达式

#include <iostream>

int main() {
    // 最简单的 lambda:无参数,无返回值
     { 
        std::cout << "Hello, Lambda!" << std::endl; 
    }();  // 直接调用
    
    // 省略空参数列表的括号
    []{ 
        std::cout << "Simplified lambda" << std::endl; 
    }();
    
    return 0;
}

1.3 将 Lambda 赋值给变量

#include <iostream>
#include <functional>

int main() {
    // 使用 auto 推导 lambda 类型
    auto greet =  {
        std::cout << "Hello from lambda!" << std::endl;
    };
    
    greet();  // 调用 lambda
    greet();  // 可以多次调用
    
    // 带参数的 lambda
    auto add = int a, int b {
        return a + b;
    };
    
    std::cout << "5 + 3 = " << add(5, 3) << std::endl;
    
    return 0;
}

2. Lambda 表达式完整语法

2.1 完整语法结构

[ 捕获列表 ] ( 参数列表 ) 可变规范(可选) 异常规范(可选) -> 返回类型(可选) { 函数体 }

完整示例

#include <iostream>

int main() {
    int x = 10;
    int y = 20;
    
    // 完整的 lambda 表达式
    auto complex_lambda = int a, int b mutable 
                         noexcept -> int {
        x = a + b;  // 修改捕获的 x(副本)
        y = a - b;  // 修改捕获的 y(引用)
        return x * y;
    };
    
    int result = complex_lambda(5, 3);
    std::cout << "Result: " << result << std::endl;
    std::cout << "Original y: " << y << std::endl;  // y 被修改
    
    return 0;
}

3. 捕获列表详解

3.1 值捕获 vs 引用捕获

值捕获 [=][变量名]
#include <iostream>

int main() {
    int a = 10, b = 20;
    
    // 值捕获:创建变量的副本
    auto value_capture =  {
        std::cout << "a = " << a << ", b = " << b << std::endl;
        // a = 5;  // 错误:值捕获的变量默认是 const
    };
    
    a = 100;  // 修改原始变量
    value_capture();  // 输出:a = 10, b = 20(捕获时的值)
    
    return 0;
}
引用捕获 [&][&变量名]
#include <iostream>

int main() {
    int a = 10, b = 20;
    
    // 引用捕获:使用变量的引用
    auto ref_capture =  {
        std::cout << "a = " << a << ", b = " << b << std::endl;
        a = 100;  // 修改原始变量
    };
    
    ref_capture();  // 输出:a = 10, b = 20
    std::cout << "After lambda: a = " << a << std::endl;  // a = 100
    
    return 0;
}

3.2 隐式捕获

隐式值捕获 [=]
int x = 1, y = 2, z = 3;

// 捕获所有外部变量(值方式)
auto lambda1 =  {
    std::cout << x + y + z << std::endl;  // 可以使用所有外部变量
    // 但不能修改,因为都是值捕获的副本
};
隐式引用捕获 [&]
int x = 1, y = 2, z = 3;

// 捕获所有外部变量(引用方式)
auto lambda2 =  {
    x = 10; y = 20; z = 30;  // 修改原始变量
};

3.3 混合捕获

#include <iostream>

int main() {
    int a = 1, b = 2, c = 3, d = 4;
    
    // 混合捕获:a 值捕获,其他引用捕获
    auto mixed1 =  {
        std::cout << "a(copy) = " << a << ", b(ref) = " << b 
                  << ", c(ref) = " << c << ", d(ref) = " << d << std::endl;
    };
    
    // 使用隐式+显式混合
    auto mixed2 =  {  // 除 b 外都值捕获,b 引用捕获
        // a, c, d 是副本,b 是引用
    };
    
    auto mixed3 =  {   // 除 a 外都引用捕获,a 值捕获
        // b, c, d 是引用,a 是副本
    };
    
    mixed1();
    return 0;
}

3.4 初始化捕获(C++14)

移动捕获
#include <memory>
#include <iostream>

int main() {
    auto ptr = std::make_unique<int>(42);
    
    // C++14 初始化捕获:移动语义
    auto lambda =  {  // p 是移动捕获的 unique_ptr
        if (p) {
            std::cout << "Value: " << *p << std::endl;
        }
    };
    
    lambda();
    // ptr 现在为 nullptr
    
    return 0;
}
在捕获时进行计算
#include <iostream>

int main() {
    int x = 10;
    int y = 20;
    
    // 捕获时进行计算
    auto lambda =  {
        std::cout << "Sum: " << sum << ", Product: " << product << std::endl;
    };
    
    lambda();
    
    return 0;
}

3.5 this 指针捕获

#include <iostream>

class MyClass {
private:
    int value = 42;
    
public:
    void demonstrate_lambda() {
        // 捕获 this 指针,访问成员变量
        auto lambda1 =  {
            std::cout << "Value: " << this->value << std::endl;
        };
        
        // 隐式捕获 this(C++20 弃用 [=] 捕获 this)
        auto lambda2 =  {  // C++17:复制 *this
            std::cout << "Copied value: " << value << std::endl;
        };
        
        lambda1();
        lambda2();
    }
};

int main() {
    MyClass obj;
    obj.demonstrate_lambda();
    return 0;
}

4. 参数列表与可变规范

4.1 参数列表

基本参数传递
#include <iostream>

int main() {
    // 带参数的 lambda
    auto adder = int a, int b -> int {
        return a + b;
    };
    
    std::cout << "5 + 3 = " << adder(5, 3) << std::endl;
    
    // 默认参数(C++14)
    auto multiplier = int a, int b = 2 {
        return a * b;
    };
    
    std::cout << "5 * 2 = " << multiplier(5) << std::endl;
    std::cout << "5 * 3 = " << multiplier(5, 3) << std::endl;
    
    return 0;
}
通用 Lambda(C++14)
#include <iostream>
#include <typeinfo>

int main() {
    // 使用 auto 参数(通用 lambda)
    auto print_type = auto value {
        std::cout << "Value: " << value 
                  << ", Type: " << typeid(value).name() << std::endl;
    };
    
    print_type(42);        // int
    print_type(3.14);      // double
    print_type("Hello");   // const char*
    
    // 多个 auto 参数
    auto add_any = auto a, auto b {
        return a + b;
    };
    
    std::cout << add_any(5, 3) << std::endl;      // 8
    std::cout << add_any(2.5, 3.7) << std::endl;  // 6.2
    
    return 0;
}

4.2 可变规范 (mutable)

修改值捕获的变量
#include <iostream>

int main() {
    int counter = 0;
    
    // 没有 mutable:不能修改值捕获的变量
    // auto bad_lambda =  {
    //     counter++;  // 错误:counter 是 const
    // };
    
    // 使用 mutable:可以修改值捕获的变量(修改的是副本)
    auto good_lambda =  mutable {
        counter++;
        std::cout << "Internal counter: " << counter << std::endl;
    };
    
    good_lambda();  // 输出:Internal counter: 1
    good_lambda();  // 输出:Internal counter: 2
    std::cout << "Original counter: " << counter << std::endl;  // 仍然是 0
    
    return 0;
}

5. 返回类型与异常规范

5.1 返回类型推导与显式指定

自动返回类型推导
#include <iostream>

int main() {
    // 返回类型自动推导为 int
    auto add = int a, int b {
        return a + b;  // 编译器推导返回类型为 int
    };
    
    // 返回类型自动推导为 double
    auto divide = double a, double b {
        if (b != 0) return a / b;
        else return 0.0;  // 所有返回路径必须类型一致
    };
    
    std::cout << add(5, 3) << std::endl;       // 8
    std::cout << divide(10.0, 3.0) << std::endl; // 3.333...
    
    return 0;
}
显式指定返回类型
#include <iostream>
#include <vector>

int main() {
    // 需要显式指定返回类型的情况
    
    // 1. 多条返回路径,类型不同但兼容
    auto safe_divide = int a, int b -> double {
        if (b == 0) return 0.0;    // double
        return a / b;              // int,但会被转换为 double
    };
    
    // 2. 复杂返回类型
    std::vector<int> numbers = {1, 2, 3};
    auto get_iterator =  -> std::vector<int>::iterator {
        return numbers.begin();
    };
    
    // 3. Lambda 体复杂,编译器难以推导
    auto complex_logic = int x -> int {
        if (x > 0) return x * 2;
        else if (x < 0) return x / 2;
        else return 0;
    };
    
    std::cout << safe_divide(5, 2) << std::endl;  // 2.5
    std::cout << complex_logic(5) << std::endl;   // 10
    
    return 0;
}

5.2 异常规范

noexcept 规范
#include <iostream>

int main() {
    // 不抛出异常的 lambda
    auto no_throw =  noexcept {
        std::cout << "This lambda doesn't throw" << std::endl;
    };
    
    // 可能抛出异常的 lambda
    auto may_throw = int x {
        if (x < 0) {
            throw std::invalid_argument("Negative value");
        }
        return x * 2;
    };
    
    try {
        no_throw();
        std::cout << may_throw(5) << std::endl;
        // std::cout << may_throw(-1) << std::endl;  // 会抛出异常
    } catch (const std::exception& e) {
        std::cout << "Exception: " << e.what() << std::endl;
    }
    
    return 0;
}

6. Lambda 与 STL 算法

6.1 与 std::function 配合使用

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

// 接受函数作为参数
void process_numbers(const std::vector<int>& numbers, 
                    std::function<void(int)> processor) {
    for (int n : numbers) {
        processor(n);
    }
}

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    
    // 将 lambda 传递给函数
    process_numbers(numbers, int n {
        std::cout << n * 2 << " ";
    });
    std::cout << std::endl;
    
    // 存储 lambda 在 std::function 中
    std::function<int(int, int)> operation;
    
    operation = int a, int b { return a + b; };
    std::cout << "Addition: " << operation(5, 3) << std::endl;
    
    operation = int a, int b { return a * b; };
    std::cout << "Multiplication: " << operation(5, 3) << std::endl;
    
    return 0;
}

6.2 在 STL 算法中的应用

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

int main() {
    std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
    
    // 1. 使用 lambda 进行排序
    std::sort(numbers.begin(), numbers.end(), int a, int b {
        return a > b;  // 降序排序
    });
    
    std::cout << "Sorted (descending): ";
    for (int n : numbers) {
        std::cout << n << " ";
    }
    std::cout << std::endl;
    
    // 2. 使用 lambda 进行查找
    auto it = std::find_if(numbers.begin(), numbers.end(), int n {
        return n % 2 == 0;  // 查找第一个偶数
    });
    
    if (it != numbers.end()) {
        std::cout << "First even number: " << *it << std::endl;
    }
    
    // 3. 使用 lambda 进行转换
    std::vector<int> squared;
    std::transform(numbers.begin(), numbers.end(), 
                   std::back_inserter(squared), int n {
        return n * n;
    });
    
    std::cout << "Squared: ";
    for (int n : squared) {
        std::cout << n << " ";
    }
    std::cout << std::endl;
    
    // 4. 使用 lambda 进行累加
    int sum = std::accumulate(numbers.begin(), numbers.end(), 0, 
                              int total, int n {
                                  return total + n;
                              });
    std::cout << "Sum: " << sum << std::endl;
    
    return 0;
}

6.3 在智能指针中的应用

#include <iostream>
#include <memory>
#include <vector>

class Resource {
public:
    Resource(int id) : id_(id) {
        std::cout << "Resource " << id_ << " created" << std::endl;
    }
    
    ~Resource() {
        std::cout << "Resource " << id_ << " destroyed" << std::endl;
    }
    
    void use() {
        std::cout << "Using resource " << id_ << std::endl;
    }
    
private:
    int id_;
};

int main() {
    std::vector<std::unique_ptr<Resource>> resources;
    
    // 创建一些资源
    for (int i = 0; i < 3; ++i) {
        resources.push_back(std::make_unique<Resource>(i));
    }
    
    // 使用 lambda 处理资源
    std::for_each(resources.begin(), resources.end(), 
                  auto& resource {
                      if (resource) {
                          resource->use();
                      }
                  });
    
    // 使用 lambda 自定义删除器
    auto custom_deleter = Resource* res {
        std::cout << "Custom delete for resource" << std::endl;
        delete res;
    };
    
    std::unique_ptr<Resource, decltype(custom_deleter)> 
        custom_resource(new Resource(99), custom_deleter);
    
    return 0;
}

7. 高级特性与技巧

7.1 模板 Lambda(C++20)

#include <iostream>
#include <vector>
#include <list>

// C++20 模板 lambda
auto print_container = []<typename T>(const T& container) {
    for (const auto& item : container) {
        std::cout << item << " ";
    }
    std::cout << std::endl;
};

// 带模板参数的 lambda
auto add_template = []<typename T>(T a, T b) -> T {
    return a + b;
};

int main() {
    std::vector<int> vec = {1, 2, 3};
    std::list<double> lst = {1.1, 2.2, 3.3};
    
    print_container(vec);  // 用于 vector<int>
    print_container(lst);  // 用于 list<double>
    
    std::cout << add_template(5, 3) << std::endl;        // int
    std::cout << add_template(2.5, 3.7) << std::endl;    // double
    
    return 0;
}

7.2 立即调用 Lambda(IILE)

#include <iostream>
#include <string>

int main() {
    // 立即调用的 lambda 表达式 (IILE)
    const std::string message =  -> std::string {
        std::string result;
        result += "Hello, ";
        result += "World!";
        return result;
    }();  // 立即调用
    
    std::cout << message << std::endl;
    
    // 用于复杂初始化
    const int computed_value = int base {
        int result = base * 2;
        result += 10;
        return result * 3;
    }(5);  // 基于参数 5 立即计算
    
    std::cout << "Computed value: " << computed_value << std::endl;
    
    return 0;
}

7.3 递归 Lambda

#include <iostream>

int main() {
    // 递归 lambda 需要使用 std::function
    std::function<int(int)> factorial;
    
    factorial = int n -> int {
        if (n <= 1) return 1;
        return n * factorial(n - 1);
    };
    
    std::cout << "5! = " << factorial(5) << std::endl;
    
    // 使用泛型 lambda 和 auto 递归(C++14)
    auto fib = auto&& self, int n -> int {
        if (n <= 1) return n;
        return self(self, n - 1) + self(self, n - 2);
    };
    
    auto fibonacci = int n { return fib(fib, n); };
    
    std::cout << "fib(10) = " << fibonacci(10) << std::endl;
    
    return 0;
}

7.4 可变参数 Lambda(C++14)

#include <iostream>

int main() {
    // 可变参数 lambda
    auto print_all = auto&&... args {
        (std::cout << ... << args) << std::endl;  // C++17 折叠表达式
    };
    
    print_all("Hello", " ", "World", " ", 2023);
    
    // 更复杂的可变参数处理
    auto sum_all = auto first, auto... rest {
        if constexpr (sizeof...(rest) == 0) {
            return first;
        } else {
            return first + sum_all(rest...);
        }
    };
    
    // 使用立即调用的 lambda 包装递归可变参数
    auto sum_wrapper = auto... args {
        auto impl = auto&& self, auto first, auto... rest {
            if constexpr (sizeof...(rest) == 0) {
                return first;
            } else {
                return first + self(self, rest...);
            }
        };
        return impl(impl, args...);
    };
    
    std::cout << "Sum: " << sum_wrapper(1, 2, 3, 4, 5) << std::endl;
    
    return 0;
}

8. 性能考虑与最佳实践

8.1 性能优化

优先使用自动类型推导
#include <chrono>
#include <functional>
#include <iostream>

void performance_test() {
    auto fast_lambda = int x { return x * x; };  // 快速:auto 推导
    
    std::function<int(int)> slow_wrapper = fast_lambda;  // 较慢:类型擦除
    
    // 在性能敏感的代码中直接使用 auto lambda
    auto start = std::chrono::high_resolution_clock::now();
    
    int result = 0;
    for (int i = 0; i < 1000000; ++i) {
        result += fast_lambda(i);  // 通常会被内联
    }
    
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    std::cout << "Time with auto lambda: " << duration.count() << " microseconds" << std::endl;
}
避免不必要的捕获
#include <iostream>

void efficient_lambda_use() {
    int important_data = 42;
    std::string large_object = "very large string data...";
    
    // 不好:捕获了不需要的大对象
    // auto inefficient =  {
    //     std::cout << important_data << std::endl;  // 只需要这个
    //     // 但 large_object 也被不必要地捕获了
    // };
    
    // 好:只捕获需要的变量
    auto efficient =  {  // 只捕获需要的变量
        std::cout << important_data << std::endl;
    };
    
    efficient();
}

8.2 最佳实践总结

代码可读性
#include <vector>
#include <algorithm>
#include <iostream>

void readability_examples() {
    std::vector<int> numbers = {5, 2, 8, 1, 9};
    
    // 好:有意义的变量名和清晰的结构
    auto is_even = int n { return n % 2 == 0; };
    auto square = int n { return n * n; };
    
    // 不好:过于复杂的内联 lambda
    // std::remove_if(numbers.begin(), numbers.end(), 
    //               int n{return n%2==0&&n>5||n<3&&n!=1;});
    
    // 好:分解复杂逻辑
    auto complex_condition = int n {
        bool is_even = (n % 2 == 0);
        bool is_large_even = is_even && (n > 5);
        bool is_small_odd = !is_even && (n < 3) && (n != 1);
        return is_large_even || is_small_odd;
    };
}

// 使用有意义的 lambda 变量名
class DataProcessor {
private:
    std::vector<int> data_;
    
public:
    void process() {
        // 有意义的 lambda 命名
        auto validate_data =  {
            return !data_.empty() && data_.size() < 1000;
        };
        
        auto transform_data = int value {
            return value * 2 + 1;
        };
        
        if (validate_data()) {
            std::transform(data_.begin(), data_.end(), 
                          data_.begin(), transform_data);
        }
    }
};
避免常见陷阱
#include <iostream>
#include <functional>
#include <memory>

void avoid_common_pitfalls() {
    // 陷阱1:悬挂引用
    std::function<int()> create_dangling_lambda() {
        int local_var = 42;
        return  { return local_var; };  // 危险:返回局部变量的引用
    }
    
    // 解决:使用值捕获或确保生命周期
    std::function<int()> create_safe_lambda() {
        int local_var = 42;
        return  { return local_var; };  // 安全:值捕获
    }
    
    // 陷阱2:在 lambda 中捕获移动的变量
    auto create_resource_handler() {
        auto resource = std::make_unique<int>(42);
        
        // 错误:捕获了即将被移动的变量
        // return  { return *resource; };
        
        // 正确:使用移动捕获
        return  { return *r; };
    }
    
    // 陷阱3:在构造函数中使用 lambda
    class Widget {
        std::function<void()> callback_;
        
    public:
        Widget() {
            // 避免在构造函数中捕获不完整的 this
            int data = 42;
            callback_ =  {  // 安全:不捕获 this
                std::cout << "Data: " << data << std::endl;
            };
        }
    };
}

8.3 现代 C++ 特性结合

与 constexpr 结合(C++17)
#include <iostream>

constexpr auto compile_time_lambda = int n {
    return n * n;
};

int main() {
    // 编译时计算
    constexpr int result = compile_time_lambda(5);
    std::cout << "Compile-time result: " << result << std::endl;
    
    // 编译时 lambda 可以用于模板参数(C++20)
    auto greater_than = []<typename T>(T threshold) {
        return T value { return value > threshold; };
    };
    
    auto is_positive = greater_than(0);
    std::cout << "5 is positive: " << is_positive(5) << std::endl;
    
    return 0;
}
与概念(Concepts)结合(C++20)
#include <concepts>
#include <iostream>

// C++20 概念约束的 lambda
auto arithmetic_operation = []<std::integral T>(T a, T b) {
    return a + b;  // 只接受整数类型
};

// 使用概念约束可变参数 lambda
auto print_arithmetic = []<std::arithmetic... Ts>(Ts... args) {
    (std::cout << ... << args) << std::endl;
};

int main() {
    std::cout << arithmetic_operation(5, 3) << std::endl;
    // arithmetic_operation(3.14, 2.71);  // 错误:浮点数不满足 std::integral
    
    print_arithmetic(1, 2.5, 3);  // 正确:支持算术类型
    
    return 0;
}

这份详细的 C++ Lambda 表达式教程涵盖了从基础语法到高级用法的所有核心内容,包括各种捕获方式、参数处理、返回类型、性能优化和最佳实践。Lambda 表达式是现代 C++ 编程中不可或缺的工具,合理使用可以显著提高代码的可读性和可维护性。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐