【c++】:Lambda 表达式介绍和使用

C++17 对 Lambda 表达式进行了多项重要扩展,使其功能更强大、使用更灵活。Lambda 表达式是一种匿名函数,用于简化代码中的回调、排序、算法参数等场景。以下从 基础语法C++17 新增特性实际应用 三个维度详细讲解:

一、Lambda 表达式基础语法

Lambda 表达式的核心结构如下(C++11 起支持):

cpp 复制代码
[capture-list](parameters) mutable noexcept -> return-type {
    // 函数体
}
  • 捕获列表(capture-list :定义 Lambda 外部变量的访问方式(如按值捕获 [x]、按引用捕获 [&y])。
  • 参数列表(parameters :与普通函数参数类似,C++14 起支持 auto 自动推导参数类型。
  • mutable :允许修改按值捕获的变量(默认按值捕获为 const)。
  • noexcept:声明函数不抛出异常。
  • 返回类型(return-type:可省略,由编译器自动推导(C++11 起)。

二、C++17 对 Lambda 的核心增强

C++17 新增了多项特性,解决了之前版本的限制,主要包括:

1. ** constexpr Lambda(常量表达式 Lambda)**
  • 特性 :Lambda 表达式可在编译期执行,支持用于 constexpr 变量初始化、模板参数等编译期场景。

  • 语法 :无需显式声明 constexpr,编译器会自动判断 Lambda 是否满足常量表达式要求(若函数体符合 constexpr 规则)。

  • 示例

    cpp 复制代码
    #include <iostream>
    
    int main() {
        // C++17 中,满足条件的 Lambda 自动为 constexpr
        auto add = [](int a, int b) { return a + b; };
    
        // 编译期计算(需编译器支持 C++17)
        constexpr int sum = add(3, 5);  // sum = 8(编译期确定)
        std::cout << sum << std::endl;
    
        return 0;
    }
  • 限制:函数体只能包含编译期可执行的代码(如无动态内存分配、无虚函数调用等)。

2. 捕获 *this(按值捕获当前对象)
  • 问题背景 :C++11/14 中,[this] 按引用捕获当前对象,若 Lambda 生命周期超过对象生命周期,会导致悬垂引用。

  • C++17 解决方案[*this] 按值捕获当前对象的副本,避免悬垂引用。

  • 示例

    cpp 复制代码
    #include <iostream>
    #include <memory>
    
    struct MyClass {
        int x = 10;
        auto getLambda() {
            // 按值捕获当前对象(*this 是对象的副本)
            return [*this]() { 
                std::cout << "x = " << x << std::endl;  // 访问副本的 x
            };
        }
    };
    
    int main() {
        auto lambda = [](){
            MyClass obj;
            return obj.getLambda();  // obj 生命周期结束,但 Lambda 持有副本
        }();
    
        lambda();  // 输出:x = 10(安全访问副本)
        return 0;
    }
3. 模板化 Lambda(泛型 Lambda 增强)
  • 特性 :C++14 已支持 auto 作为参数类型(泛型 Lambda),C++17 进一步允许显式模板参数列表,支持更复杂的泛型逻辑。

  • 语法[]<template-params>(params) { ... }

  • 示例

    cpp 复制代码
    #include <iostream>
    #include <vector>
    #include <list>
    
    int main() {
        // 显式模板参数的 Lambda(C++17 新增)
        auto printSize = []<typename T>(const T& container) {
            std::cout << "Size: " << container.size() << std::endl;
        };
    
        std::vector<int> vec = {1, 2, 3};
        std::list<double> lst = {1.1, 2.2};
        printSize(vec);  // 输出:Size: 3(T 推导为 vector<int>)
        printSize(lst);  // 输出:Size: 2(T 推导为 list<double>)
        return 0;
    }
  • 优势 :比 auto 参数更灵活,支持模板特化、模板参数约束(C++20 进一步增强)等。

4. Lambda 在非类型模板参数中使用
  • 特性 :C++17 允许 Lambda 作为非类型模板参数(需满足 constexpr 要求)。

  • 示例

    cpp 复制代码
    #include <iostream>
    
    // 模板接受 Lambda 作为参数
    template <auto Lambda>
    void callLambda() {
        Lambda();  // 调用传入的 Lambda
    }
    
    int main() {
        // 传递 constexpr Lambda 作为模板参数
        callLambda<[](){ std::cout << "Lambda called!\n"; }>();  // 输出:Lambda called!
        return 0;
    }
5. 捕获初始化(初始化捕获的增强)
  • 特性 :C++14 已支持初始化捕获(如 [x = 10](){ ... }),C++17 允许捕获中使用 auto 自动推导类型,简化代码。

  • 示例

    cpp 复制代码
    #include <iostream>
    #include <string>
    
    int main() {
        std::string s = "hello";
        // 初始化捕获 + auto 推导(C++17 允许)
        auto lambda = [str = std::move(s)]() {  // str 类型自动推导为 string
            std::cout << str << std::endl;
        };
        lambda();  // 输出:hello
        return 0;
    }

三、C++17 Lambda 的实际应用场景

1. 编译期计算

利用 constexpr Lambda 实现编译期逻辑,提升运行时效率:

cpp 复制代码
constexpr auto factorial = [](int n) {
    int res = 1;
    for (int i = 2; i <= n; ++i) res *= i;
    return res;
};

constexpr int f5 = factorial(5);  // 120(编译期计算)
2. 安全捕获对象

使用 [*this] 避免对象生命周期问题,尤其在异步编程中:

cpp 复制代码
#include <future>

struct Task {
    int value = 5;
    auto asyncTask() {
        // 按值捕获 this,确保异步执行时对象已销毁也安全
        return std::async(std::launch::async, [*this]() {
            return value * 2;  // 使用对象副本的 value
        });
    }
};

int main() {
    auto future = [](){
        Task t;
        return t.asyncTask();  // t 销毁,但 Lambda 持有副本
    }();
    std::cout << future.get() << std::endl;  // 输出:10
    return 0;
}
3. 泛型算法适配

结合模板化 Lambda 简化泛型代码,适配不同容器或数据类型:

cpp 复制代码
#include <algorithm>
#include <vector>

// 通用过滤函数,接受 Lambda 作为过滤条件
template <typename Container, typename Filter>
auto filter(const Container& c, Filter f) {
    Container res;
    std::copy_if(c.begin(), c.end(), std::back_inserter(res), f);
    return res;
}

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5, 6};
    // 模板化 Lambda 作为过滤条件(同时支持奇偶数判断)
    auto isEven = [](auto x) { return x % 2 == 0; };
    auto evens = filter(nums, isEven);  // evens = {2,4,6}
    return 0;
}

四、C++17 Lambda 与之前版本的对比

特性 C++11/14 支持度 C++17 支持度
constexpr Lambda 不支持(需手动包装) 自动支持(符合条件时)
按值捕获 *this 不支持(仅 [this] 按引用) 支持 [*this] 按值捕获副本
显式模板参数 不支持(仅 auto 参数) 支持 <typename T> 模板参数
作为非类型模板参数 不支持 支持(需 constexpr
初始化捕获 auto 部分支持(需显式类型) 完全支持 auto 推导

总结

C++17 显著增强了 Lambda 表达式的功能,使其从"简单匿名函数"升级为支持 编译期计算安全对象捕获复杂泛型逻辑 的强大工具。这些特性尤其在模板编程、异步编程、泛型算法中能大幅简化代码,提升可读性和安全性。掌握 C++17 Lambda 是现代 C++ 开发的重要技能。

相关推荐
麦麦鸡腿堡3 小时前
Java的动态绑定机制(重要)
java·开发语言·算法
Tiger_shl3 小时前
C# 预处理指令 (# 指令) 详解
开发语言·c#
汉克老师3 小时前
GESP2025年9月认证C++四级( 第三部分编程题(1)排兵布阵)
c++·算法·gesp4级·gesp四级
@Kerry~3 小时前
phpstudy .htaccess 文件内容
java·开发语言·前端
CRMEB系统商城4 小时前
CRMEB多商户系统(PHP)v3.3正式发布,同城配送上线[特殊字符]
java·开发语言·小程序·php
sali-tec4 小时前
C# 基于halcon的视觉工作流-章45-网格面划痕
开发语言·算法·计算机视觉·c#
一壶浊酒..4 小时前
python 爬取百度图片
开发语言·python·百度
机器视觉知识推荐、就业指导4 小时前
C语言中的预编译是什么?何时需要预编译?
c语言·开发语言
·心猿意码·4 小时前
C++智能指针解析
开发语言·c++