C ++ Lambdas:“可变”和按引用捕获之间的区别


67

在C ++中,您可以像这样声明lambda:

int x = 5;
auto a = [=]() mutable { ++x; std::cout << x << '\n'; };
auto b = [&]()         { ++x; std::cout << x << '\n'; };

两者都让我修改x,所以有什么区别?

Answers:


88

怎么了

第一个只会修改自己的副本,x而外部不会x更改。第二个将修改外部x

尝试以下各项后添加打印语句:

a();
std::cout << x << "----\n";
b();
std::cout << x << '\n';

预计将打印:

6
5
----
6
6

为什么

考虑一下lambda可能会有所帮助

表达式提供了一种创建简单函数对象的简洁方法

(请参阅标准的[expr.prim.lambda])

他们有

[...]公共内联函数调用运算符[...]

声明为const成员函数,但仅

[...]当且仅当lambda表达式的参数声明子句后没有mutable

你可以想到

    int x = 5;
    auto a = [=]() mutable { ++x; std::cout << x << '\n'; };

==>

    int x = 5;

    class __lambda_a {
        int x;
    public:
        __lambda_a () : x($lookup-one-outer$::x) {}
        inline void operator() { ++x; std::cout << x << '\n'; }     
    } a;

    auto b = [&]()         { ++x; std::cout << x << '\n'; };

==>

    int x = 5;

    class __lambda_b {
        int &x;
    public:
        __lambda_b() : x($lookup-one-outer$::x) {}
        inline void operator() const { ++x; std::cout << x << '\n'; }         
        //                     ^^^^^
    } b;

问:但是如果它是一个const函数,为什么我仍然可以更改x

答:您只是在改变外面x。lambda本身x是一个引用,并且该操作++x不会修改reference,而是引用的值

之所以可行,是因为在C ++中,指针/引用的常量不会更改通过它看到的指针/引用的常量。


1
尼斯和彻底。但是我想你有a一些你想要的地方b。而且您的匿名类类型的对象没有初始化程序。可能想解释一下我们假装lambda_a()是一个构造函数,尽管该类没有名称。
aschepler 2013年

@aschepler:感谢您的建议:)我改成了保留名。
塞巴斯蒂安·马赫

3
@ AnoopK.Prabhu:在最后一个示例中,const实际上没有什么区别。您总是通过引用对引用对象进行变异。再举一个例子:int main () { int x; auto a = [=]() { ++x; }; }。g ++将为此给出一条错误消息,因为这[=]意味着生成的函数对象获得了自己的成员变量x,但是缺少mutable意味着生成了const成员函数调用运算符的功能,因此x不能分配。
塞巴斯蒂安·马赫

1
优秀的!+1尤其适用于上述注释,并且指针/引用的常量不会更改通过它看到的指针/引用的常量。在auto c = [=]() { ++x; };上面加上这种情况将更清楚地说明为什么mutableconst会有所不同。
legends2k 2014年

3
真正的问题是,为什么与C ++中的其他所有约定相比,约定却相反。mutable资格默认情况下,const规范应该是明确的。
v.oddou 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.