欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 美景 > C++ lambda表达式的捕获原理

C++ lambda表达式的捕获原理

2025/5/8 14:27:21 来源:https://blog.csdn.net/qq_47355554/article/details/147773350  浏览:    关键词:C++ lambda表达式的捕获原理

目录

  • C++ Lambda表达式捕获机制的详细原理分析
    • Lambda的本质机制
    • 关键实现细节
    • 特殊捕获方式
    • [ ]空捕获
    • [&]全引用捕获
    • [=]全值(拷贝)捕获
    • 混合捕获
      • [=,&variable]拷贝及部分引用捕获
      • [&,variable]引用及部分拷贝捕获
    • 显式捕获
      • [variable]拷贝捕获部分变量
      • [&variable]引用捕获部分变量
    • [this]拷贝捕获this

C++ Lambda表达式捕获机制的详细原理分析

Lambda的本质机制

Lambda表达式本质是编译器生成的匿名类,通过重载operator()实现函数调用运算符。捕获列表中的变量会被转换为该类的成员变量:

// 原始Lambda
auto f = [x](){ return x+1; };// 等效编译器生成类
class __lambda_XXXX {int x;  // 捕获变量存储为成员变量
public:int operator()() const { return x+1; }
};

关键实现细节

  1. 捕获时机:发生在lambda表达式定义时,后续外部变量修改不影响已捕获的值(引用捕获除外)

  2. 成员初始化

    __lambda_XXXX{var} // 值捕获时调用拷贝构造
    __lambda_XXXX{std::move(var)} // C++14支持移动捕获
    
  3. 生命周期问题:引用捕获必须确保外部变量生命周期长于lambda对象

  4. 性能影响

    • 值捕获:可能引起拷贝开销
    • 引用捕获:无拷贝开销,但有悬空引用风险

特殊捕获方式

  1. 初始化捕获(C++14)

    [ptr = std::move(unique_ptr)]{} // 移动语义捕获
    
  2. 结构化绑定捕获(C++17):

    auto [a, b] = getPair();
    auto f = [a, b](){...}; 
    

[ ] 不截取任何变量
[&]截取外部作用域中所有变量,并作为引用在函数体中使用
[=] 截取外部作用域中所有变量,并拷贝一份在函数体中使用
[=,&variable]   截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对以逗号分隔variable使用引用
[&,variable] 以引用的方式捕获外部作用域中所有变量,对以逗号分隔的变量列表variable使用值的方式捕获
[variable] 对以逗号分隔的变量列表variable使用值的方式捕获
[&variable] 对以逗号分隔的变量列表variable使用引用的方式捕获
[this] 截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。

cppinsights将高级C++代码转换为其等效的低级代码表示;

[ ]空捕获

  • 原理:不生成任何成员变量,相当于没有状态的函数对象
  • 限制:只能访问全局变量和静态变量

[&]全引用捕获

  • 原理:所有捕获变量存储为引用类型成员

  • 底层实现:

    class __lambda_7_11 {std::string& s; // 引用类型成员int& a;         // 引用类型成员
    public:void operator()() const {s += std::to_string(a);a += 100; // 直接修改原变量}
    };
    
  • 特性:修改会影响外部变量,需注意生命周期问题

源:

#include <string>
#include <iostream>
int main()
{int a = 10;std::string s = "hello";auto f = [&](){s += std::to_string(a);a += 100;};std::cout << a << std::endl;f();return 0;
}

cppinsights展开:

#include <string>
#include <iostream>
int main()
{int a = 10;std::basic_string<char> s = std::basic_string<char>("hello", std::allocator<char>());class __lambda_7_11{public:inline /*constexpr */ void operator()() const{s.operator+=(std::to_string(a));a = a + 100;}private:std::basic_string<char>& s;int& a;public:__lambda_7_11(std::basic_string<char>& _s, int& _a): s{ _s }, a{ _a }{}};  // 函数对象__lambda_7_11 f = __lambda_7_11{ s, a };          // 构造函数std::cout.operator<<(a).operator<<(std::endl);  // 输出f.operator()();                                 // 调用函数return 0;
}

[=]全值(拷贝)捕获

  • 原理:所有变量存储为值类型成员

  • 底层实现:

    class __lambda_9_11 {std::string s;  // 值类型成员int a;          // 值类型成员 
    public:void operator()() const { // 默认const修饰std::cout << s << a;  // 只能读取不能修改}
    };
    
  • mutable修饰:移除operator()的const限定,允许修改副本

    mutable lambda允许:
    void operator()() { // 非const版本a += 100; // 修改副本值
    }
    

源:

#include <string>
#include <iostream>int main()
{int a = 10;std::string s = "hello";//  默认是常函数void operator()() constauto f = [=]() {std::cout << "Lambda = " << s << std::endl;std::cout << "Lambda = " << a << std::endl;// 报错:表达式必须是可修改的左值// a += 100;};// 捕获时机:发生在lambda表达式定义时,后续外部变量修改不影响已捕获的值(引用捕获除外)a = 100;std::cout << "a = " << a << std::endl;f();// 设置mutable,使得void operator()()可以修改捕获的变量的副本(非本身)auto f2 = [=]()mutable {std::cout << "Lambda = " << s << std::endl;std::cout << "Lambda = " << a << std::endl;// 这里的a是外部变量的副本s += std::to_string(a); // 无法改变外部变量sa += 100;				// 可以改变外部变量astd::cout << "Lambda = " << s << std::endl;std::cout << "Lambda = " << a << std::endl;};f2();// 没有改变外部变量std::cout << "s = " << s << std::endl;// s = hello std::cout << "a = " << a << std::endl;// a = 10return 0;
}
/*
a = 100
Lambda = hello
Lambda = 10
Lambda = hello
Lambda = 100
Lambda = hello100
Lambda = 200
s = hello
a = 100
*/

cppinsights展开:

#include <string>
#include <iostream>int main()
{int a = 10;std::basic_string<char> s = std::basic_string<char>("hello", std::allocator<char>());class __lambda_9_11{public:inline /*constexpr */ void operator()() const{std::operator<<(std::operator<<(std::cout, "Lambda = "), s).operator<<(std::endl);std::operator<<(std::cout, "Lambda = ").operator<<(a).operator<<(std::endl);}private:std::basic_string<char> s;int a;public:// inline __lambda_9_11 & operator=(const __lambda_9_11 &) /* noexcept */ = delete;// inline ~__lambda_9_11() noexcept = default;__lambda_9_11(const std::basic_string<char>& _s, int& _a): s{ _s }, a{ _a }{}};__lambda_9_11 f = __lambda_9_11{ s, a };// 捕获时机:发生在lambda表达式定义时,后续外部变量修改不影响已捕获的值(引用捕获除外)a = 100;std::operator<<(std::cout, "a = ").operator<<(a).operator<<(std::endl);f.operator()();class __lambda_22_12{public:inline /*constexpr */ void operator()(){std::operator<<(std::operator<<(std::cout, "Lambda = "), s).operator<<(std::endl);std::operator<<(std::cout, "Lambda = ").operator<<(a).operator<<(std::endl);s.operator+=(std::to_string(a));a = a + 100;std::operator<<(std::operator<<(std::cout, "Lambda = "), s).operator<<(std::endl);std::operator<<(std::cout, "Lambda = ").operator<<(a).operator<<(std::endl);}private:std::basic_string<char> s;int a;public:// inline __lambda_22_12 & operator=(const __lambda_22_12 &) /* noexcept */ = delete;// inline ~__lambda_22_12() noexcept = default;__lambda_22_12(const std::basic_string<char>& _s, int& _a): s{ _s }, a{ _a }{}};__lambda_22_12 f2 = __lambda_22_12{ s, a };f2.operator()();std::operator<<(std::operator<<(std::cout, "s = "), s).operator<<(std::endl);std::operator<<(std::cout, "a = ").operator<<(a).operator<<(std::endl);return 0;
}

混合捕获

形式原理成员变量类型
[=, &var]除var外全值捕获,var引用捕获值类型成员 + var引用成员
[&, var]除var外全引用捕获,var值捕获引用类型成员 + var值成员
[var1, &var2]显式指定值/引用捕获var1值成员 + var2引用成员

示例混合捕获展开代码:

// [=,&s] 捕获
class __lambda_9_11 {std::string& s; // 引用捕获int a;          // 值捕获
};

[=,&variable]拷贝及部分引用捕获

源:

#include <string>
#include <iostream>int main()
{int a = 10;std::string s = "hello";//  默认是常函数void operator()() constauto f = [=,&s]() {s += std::to_string(a);std::cout << a << std::endl;// 报错:表达式必须是可修改的左值// a += 100;};std::cout << s << std::endl;f();std::cout << s << std::endl;	// hello10return 0;
}

cppinsights展开:

#include <string>
#include <iostream>int main()
{int a = 10;std::basic_string<char> s = std::basic_string<char>("hello", std::allocator<char>());class __lambda_9_11{public:inline /*constexpr */ void operator()() const{s.operator+=(std::to_string(a));std::cout.operator<<(a).operator<<(std::endl);}private:std::basic_string<char>& s;int a;public:__lambda_9_11(std::basic_string<char>& _s, int& _a): s{ _s }, a{ _a }{}};__lambda_9_11 f = __lambda_9_11{ s, a };std::operator<<(std::cout, s).operator<<(std::endl);f.operator()();std::operator<<(std::cout, s).operator<<(std::endl);return 0;
}

[&,variable]引用及部分拷贝捕获

源:

#include <string>
#include <iostream>int main()
{int a = 10;std::string s = "hello";//  默认是常函数void operator()() constauto f = [&,a]() {s += std::to_string(a);std::cout << a << std::endl;// 报错:表达式必须是可修改的左值;“a” : 无法在非可变 lambda 中修改通过复制捕获// a += 100;};std::cout << s << std::endl;	// hellof();std::cout << s << std::endl;	// hello10return 0;
}

cppinsights展开:

#include <string>
#include <iostream>int main()
{int a = 10;std::basic_string<char> s = std::basic_string<char>("hello", std::allocator<char>());class __lambda_9_11{public:inline /*constexpr */ void operator()() const{s.operator+=(std::to_string(a));std::cout.operator<<(a).operator<<(std::endl);}private:int a;std::basic_string<char>& s;public:__lambda_9_11(int& _a, std::basic_string<char>& _s): a{ _a }, s{ _s }{}};__lambda_9_11 f = __lambda_9_11{ a, s };std::operator<<(std::cout, s).operator<<(std::endl);f.operator()();std::operator<<(std::cout, s).operator<<(std::endl);return 0;
}

显式捕获

  • 值捕获

    [var]
    
    • 生成const成员变量
    • 需mutable才能修改副本,修改不影响外部变量
  • 引用捕获

    [&var]
    
    • 生成引用类型成员
    • 修改直接影响外部变量

[variable]拷贝捕获部分变量

源:

#include <string>
#include <iostream>int main()
{int a = 10;std::string s = "hello";//  默认是常函数void operator()() constauto f = [a]() {// 报错:封闭函数局部变量不能在 lambda 体中引用,除非其位于捕获列表中// std::cout << s << std::endl;std::cout << a << std::endl;// 报错:表达式必须是可修改的左值;“a” : 无法在非可变 lambda 中修改通过复制捕获// a += 100;};std::cout << s << std::endl;	// hellof();return 0;
}

cppinsights展开:

#include <string>
#include <iostream>int main()
{int a = 10;std::basic_string<char> s = std::basic_string<char>("hello", std::allocator<char>());class __lambda_9_11{public:inline /*constexpr */ void operator()() const{std::cout.operator<<(a).operator<<(std::endl);}private:int a;public:__lambda_9_11(int& _a): a{ _a }{}};__lambda_9_11 f = __lambda_9_11{ a };std::operator<<(std::cout, s).operator<<(std::endl);f.operator()();return 0;
}

[&variable]引用捕获部分变量

源:

#include <string>
#include <iostream>int main()
{int a = 10;std::string s = "hello";//  默认是常函数void operator()() constauto f = [&a]() {// 报错:封闭函数局部变量不能在 lambda 体中引用,除非其位于捕获列表中// std::cout << s << std::endl;a += 100;};std::cout << a << std::endl;		// 10f();std::cout << a << std::endl;		// 110return 0; 
}

cppinsights展开:

#include <string>
#include <iostream>int main()
{int a = 10;std::basic_string<char> s = std::basic_string<char>("hello", std::allocator<char>());class __lambda_9_11{public:inline /*constexpr */ void operator()() const{a = a + 100;}private:int& a;public:__lambda_9_11(int& _a): a{ _a }{}};__lambda_9_11 f = __lambda_9_11{ a };std::cout.operator<<(a).operator<<(std::endl);f.operator()();std::cout.operator<<(a).operator<<(std::endl);return 0;
}

[this]拷贝捕获this

  • 原理:捕获当前对象的this指针

  • 实现方式:

    class __lambda_13_12 {MyClass* __this; // 存储this指针
    public:void operator()() const {__this->a += 100; // 访问成员变量}
    };
    
  • 注意:当使用[=][&]时会隐式捕获this

源:

#include <string>
#include <iostream>class MyClass
{
public:MyClass() = default;~MyClass() = default;void lambdaCapture(){//  默认是常函数void operator()() constauto f = [this]() {// 报错:封闭函数局部变量不能在 lambda 体中引用,除非其位于捕获列表中// std::cout << s << std::endl;a += 100;};std::cout << a << std::endl;		// 10f();std::cout << a << std::endl;		// 110}
private:int a = 10;std::string s = "hello";
};int main()
{MyClass().lambdaCapture();return 0;
}

cppinsights展开:

#include <string>
#include <iostream>class MyClass
{public:inline constexpr MyClass() noexcept(false) = default;inline ~MyClass() noexcept = default;inline void lambdaCapture(){class __lambda_13_12{public:inline /*constexpr */ void operator()() const{__this->a = __this->a + 100;}private:MyClass* __this;public:__lambda_13_12(MyClass* _this): __this{ _this }{}};__lambda_13_12 f = __lambda_13_12{ this };std::cout.operator<<(this->a).operator<<(std::endl);f.operator()();std::cout.operator<<(this->a).operator<<(std::endl);}private:int a;std::basic_string<char> s;
public:
};int main()
{MyClass().lambdaCapture();return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词