C++中lambda使用mutable关键字详解

C++中lambda使用mutable关键字详解

《C++初学者指南-5.标准库(第二部分)–更改元素算法》中,讲“generate”算法时有下面这段代码:

auto gen = [i=0]() mutable { i += 2; return i; };
std::vector<int> v;
v.resize(7,0);
generate(begin(v)+1, begin(v)+5, gen);
for (int x : v) { cout << x << ' '; }  // 0 2 4 6 8 0 0

第一行的lambda中使用了mutable关键字,很多人不知道其中含义,这里详细解释一下。
lambda表达式,最终会被C++编译器转换成一个匿名函数类,函数类又叫仿函数,就是重载了函数调用符:operator()的类。
可以像函数一样使用这个类的对象实例。在编译器的转换中会默认将值捕获方式的lambda转换的operator()函数的cv限定符设为const,
也就是operator()函数内是不能修改捕获的变量值的,匿名类中的重载operator()成员函数就是lambda函数本体。如果lambda表达式未使用mutable修饰,则operator()函数是const类型的,使用mutable可以解除该限制。这样就可以在lambda内修改捕获的变量值了。
我们通过Complie Explorer的反汇编功能可以清楚的看到这一点:

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

int main () {
    auto gen = [i=0]() mutable { i=i+2; return i; };
    std::cout << gen() << "\n";
}

反汇编后如下:

main::{lambda()#1}::operator()():    //注意operator()函数定义时没有const
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        lea     edx, [rax+2]
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], edx
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        pop     rbp
        ret
.LC0:
        .string "\n"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 0
        lea     rax, [rbp-4]
        mov     rdi, rax
        call    main::{lambda()#1}::operator()()          
        mov     esi, eax
        mov     edi, OFFSET FLAT:std::cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     esi, OFFSET FLAT:.LC0
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        mov     eax, 0
        leave
        ret

我们再看一下不加mutable的代码:

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

int main () {
    auto gen = [i=0]() { return i; };
    std::cout << gen() << "\n";
}

反汇编如下:

main::{lambda()#1}::operator()() const:  //注意定义函数时有const
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        pop     rbp
        ret
.LC0:
        .string "\n"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 0
        lea     rax, [rbp-4]
        mov     rdi, rax
        call    main::{lambda()#1}::operator()() const
        mov     esi, eax
        mov     edi, OFFSET FLAT:std::cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     esi, OFFSET FLAT:.LC0
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        mov     eax, 0
        leave
        ret

如果文章对您有用,请随手点个赞,谢谢!^_^
在这里插入图片描述

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值