芊千涵蕴 發表於 2026-3-17 16:24:00

Lambda表达式

<h2 id="用法">用法</h2>
<p>C++11加入了Lambda表达式,这是现代编程语言的一个特点,lambda表达式的优点:</p>
<ul>
<li>声明式的编程风格:就地匿名定义目标函数或者函数对象,不需要额外写一个声明函数或者函数对象。类似匿名内部类。</li>
<li>简洁:避免了代码膨胀和功能分散,让开发更加高效。</li>
<li>在需要的时间和地点实现功能闭包,是程序更加灵活。</li>
</ul>
<p>lambda表达式定义了一个匿名函数,并且可以捕获一定范围内的变量。</p>
<p>语法形式:</p>
<pre><code class="language-c++">(params)opt-&gt;ret{body;};
</code></pre>
<p>其中capture是捕获列表,params是参数列表,opt是函数选项,ret是返回值类型,body是函数体;下面逐个详细介绍。</p>
<ul>
<li>
<p>捕获列表capture:捕获一定范围内的变量,具体使用方式</p>
<p>[]:不捕获任何变量。</p>
<p>[&amp;]:捕获外部作用域中所有变量,并作为引用在函数体内使用(按引用捕获)</p>
<p>[=]:捕获外部作用域中所有变量,并作为副本在函数体内使用(按值捕获),<mark>拷贝的副本在匿名函数体内部是只读的</mark>。如果想要修改需要添加函数选项mutable。</p>
<pre><code class="language-c++">#include &lt;iostream&gt;
using namespace std;

void func(int x, int y) {
        int a = 7;
        int b = 9;
        [=, &amp;x](int z) mutable {
                int c = a;
                int d = x;
                b++;
                cout &lt;&lt; "bb: " &lt;&lt; b &lt;&lt; endl;
                }(88);    //没有(88)就只是匿名函数的定义,并没有调用。(88)是调用。因为有参数,所有传入的参数是88,如果没有参数就是();
        cout &lt;&lt; "b: " &lt;&lt; b &lt;&lt; endl;
}

int main() {
        func(1, 2);
        system("pause");
        return 0;
}
</code></pre>
<p>如果没有mutable变量b++就会报错。且这里是按值捕获所以这里在lambda里面的修改不会影响到外部变量。</p>
<p>[=,&amp;foo]:按值捕获外部作用域中所有变量,并按照引用捕获外部变量foo。</p>
<p>:按值捕获bar变量,同时不捕获其他变量。</p>
<p>[&amp;bar]:按引用捕获bar变量,同时不捕获其他变量。</p>
<p>:捕获当前类中的this指针。让lambda表达式拥有和当前类成员函数同样的访问权限。如果已经使用了&amp;或者=,默认添加此选项。</p>
<pre><code class="language-c++">#include &lt;iostream&gt;
using namespace std;

class Test {
public:
        void output(int x, int y) {
                auto x1 = [] {return m_number; };                  //error
                auto x2 = [=] {return m_number + x + y; };         //ok
                auto x3 = [&amp;] {return m_number + x + y; };         //ok
                auto x4 = {return m_number; };                //ok
                auto x5 = {return m_number + x + y; };      //error
                auto x6 = {return m_number + x + y; };//ok
                auto x7 = {return m_number++; };            //ok
        }
        int m_number = 100;
};
</code></pre>
<p>x1:错误,没有捕获外部变量,不能使用类成员 m_number<br>
x2:正确,以值拷贝的方式捕获所有外部变量<br>
x3:正确,以引用的方式捕获所有外部变量<br>
x4:正确,捕获this指针,可访问对象内部成员<br>
x5:错误,捕获this指针,可访问类内部成员,没有捕获到变量x,y,因此不能访问。<br>
x6:正确,捕获this指针,x,y<br>
x7:正确,捕获this指针,并且可以修改对象内部变量的值</p>
</li>
<li>
<p>参数列表params:和普通函数的参数列表一样,如果没有参数参数列表可以省略不写。</p>
<pre><code class="language-c++">auto f = [](){return 1;}//没有参数,参数列表为空
auto f = []{return 1;}    //没有参数,参数列表省略不写。
</code></pre>
</li>
<li>
<p>函数选项opt:</p>
<p>有两个类型 mutable 和 exception 两种,不需要可以省略。</p>
<p>mutable:可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。</p>
<p>exception:指定函数抛出的异常,如抛出整数类型的异常,可以使用<code>throw()</code>;</p>
</li>
<li>
<p>返回值类型:在C++11中,lambda表达式的返回值是通过返回值后置语法来定义的。</p>
<p>很多时候,lambda表达式的返回值是非常明显的,因此在c++11中允许省略lambda表达式的返回。</p>
<pre><code class="language-c++">//完整的lambda表达式定义
auto f = [](int a) -&gt; int{
    return a+10;
};

//忽略返回值的lambda表达式定义
auto f = [](int a) {
    return a+10;
};
</code></pre>
<p>一般情况下,不指定lambda表达式的返回值,编译器会根据return语句自动推导返回值的类型,但需要注意的是<mark>初始化列表不能用于返回值的自动推导</mark>。</p>
<pre><code class="language-c++">//可以自动推导出返回值类型
auto f = [](int i){
    return i;
}

//不能推导出返回值类型
auto f1 = [](){
    return {1, 2};//基于列表初始化推导返回值,error。
}
</code></pre>
<p>因为通过初始化列表可以初始化的类型很多,比如初始化一个数组int[],初始化一个结构体等很多,所以不能确定初始化的类型,因此需要把初始化的类型写出来。</p>
</li>
<li>
<p>函数体:函数的实现,这部分不能省略,但函数体可以为空。</p>
</li>
</ul>
<h2 id="lambda本质">lambda本质</h2>
<p>lambda可以看作是一种 可调用对象 。</p>
<p>C++中的可调用对象有四种:(1)函数指针、(2)仿函数、(3)可以转换为函数指针的函数对象、(4)类里面的成员函数以及类里面的成员变量。</p>
<p>在c++中lambda可以被看做是一个仿函数。operator()的类。</p>
<p>按照c++标准,lambda表达式的operator()默认是const的,一个const成员函数是无法修改成员变量值的。</p>
<p>mutable选项的作用就是在于取消operator()的const属性。</p>
<p>因为lambda表达式在c++中会被看作是一个仿函数,因此可以使用<code>std::function</code>(可调用函数包装器)进行包装称为函数指针,或者用<code>std::bind</code>绑定称为仿函数;但是如果没有捕获列表可以直接当一个函数指针看待。</p>
<pre><code class="language-c++">#include &lt;iostream&gt;
#include &lt;functional&gt;
using namespace std;

void func(int x, int y) {
        int a;
        int b;
        using ptr = void(*)(int);   //定义一个函数指针。
        ptr p1 = [](int x) {
                cout &lt;&lt; "x:" &lt;&lt; x &lt;&lt; endl;
                };
        p1(11);

        function&lt;void(int)&gt; fff = [=](int x) {   //function&lt;返回值类型(参数类型)&gt;
                cout &lt;&lt; "x:" &lt;&lt; x &lt;&lt; endl;
                };
        fff(11);

        function&lt;void(int)&gt; ffu = bind([=](int x) {   //也可以用auto进行推导类型,用auto推导的类型和function&lt;void(int)&gt;不是一个类型。因为auto推导的是仿函数类型,function&lt;void(int)&gt;是包装器对象类型。但是包装器类型可以包装auto推导出的类型
                cout &lt;&lt; "x: " &lt;&lt; x &lt;&lt; endl;
                },placeholders::_1);          //placeholders::_1是一个占位符         
        ffu(11);

}

int main() {
        system("pause");
        return 0;
}
</code></pre><br><br>
来源:https://www.cnblogs.com/ggkx/p/19730227
頁: [1]
查看完整版本: Lambda表达式