23

我在下面的函数中有一个小的“lambda 表达式”:

int main()
{
    int x = 10;
    auto lambda = [=] () { return x + 3; };
}

下面是为上述 lambda 表达式生成的“匿名闭包类”。

int main()
{
    int x = 10;

    class __lambda_3_19
    {
        public: inline /*constexpr */ int operator()() const
        {
            return x + 3;
        }

        private:
            int x;

        public: __lambda_3_19(int _x) : x{_x}
          {}

    };

    __lambda_3_19 lambda = __lambda_3_19{x};
}

编译器生成的闭包“operator()”是隐式的 const。为什么标准委员会const默认做出来?

4

3 回答 3

18

在 open-std.org 上找到 Herb Sutter 的这篇论文,讨论了这个问题。

奇怪的一对:按值捕获注入的 const 和古怪的可变
变量 考虑这个稻草人示例,程序员按值捕获局部变量并尝试修改捕获的值(它是 lambda 对象的成员变量):

int val = 0;
auto x = [=]( item e ) // look ma, [=] means explicit copy
 { use( e, ++val ); }; // error: count is const, need ‘mutable’
auto y = [val]( item e ) // darnit, I really can’t get more explicit
 { use( e, ++val ); }; // same error: count is const, need ‘mutable’

添加此功能似乎是出于担心用户可能没有意识到他得到了副本,特别是由于 lambda 是可复制的,因此他可能会更改不同的 lambda 副本。

上面的引用和示例说明了为什么标准委员会可能默认设置它const并要求mutable对其进行更改。

于 2018-11-23T11:57:13.813 回答
10

来自cppreference

除非mutable在 lambda 表达式中使用了关键字,否则函数调用运算符是 const 限定的,并且复制捕获的对象不能从 this 内部修改operator()

在您的情况下,副本捕获的任何内容都无法修改。

我想,如果你写一些东西

int x = 10;

auto lambda = [=] () mutable { x += 3; return x; };

应该const消失

- 编辑 -

OP精确

我已经知道添加 mutable 可以解决问题。问题是我想了解默认情况下使 lambda 不可变的原因。

我不是语言律师,但这似乎很明显:如果你operator()不做const,你就不能做某事

template <typename F>
void foo (F const & f)
 { f(); }

// ...

foo([]{ std::cout << "lambda!" << std::endl; });

我的意思是......如果operator()不是const,你不能使用 lambdas 传递它们作为const参考。

当不是严格需要时,应该是一个不可接受的限制。

于 2018-11-23T11:39:34.460 回答
0

我认为,这只是为了避免在 lambda 中的变量不引用最初捕获的内容时造成混淆。从词法上讲,这样的变量就好像在其“原始”范围内。复制主要是为了延长对象的生命周期。当捕获不是通过复制时,它指的是原始并且对原始应用修改,并且不会因为两个不同的对象而混淆(其中一个是隐式引入的),并且它是 lambda 的 const 函数调用运算符所允许的。

于 2018-11-23T14:05:47.410 回答