Lambda Capture by Value强制所有作用域对象为常量

Lambda Capture by Value forces all scoped object to const

本文关键字:作用域 对象 常量 Capture by Value Lambda      更新时间:2023-10-16

我本来打算用C++编写一个记忆模式,结果采用了以下方法

std::function<int(int)> Memoize(std::function<int(int)> fn)
    {
        std::map<int, int> memo;
        std::function<int(int)> helper = [=](int pos) 
        {
            if (memo.count(pos) == 0)
            {
                memo[pos] = fn(pos);
            }
            return memo[pos];
        };
        return helper;
    }

奇怪的是,我的编译器VS 2012拒绝编译,出现以下错误

1>Source1.cpp(24): error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const std::map<_Kty,_Ty>' (or there is no acceptable conversion)

在我看来,编译器故意将所有按值捕获的内容作为const对象。我找不到任何关于这种行为的文献。

有人能帮助我了解这里可能发生的事情吗?

Lambdas的行为或多或少类似于函数对象;像函数对象一样,它们有一个函数调用运算符,即operator()。对于非mutable lambdas,此函数为const:

[expr.prim.lambda]

5非泛型lambda表达式的闭包类型具有公共内联函数调用运算符[…]此函数调用运算符或运算符模板被声明为const(9.3.1)当且仅当lambda表达式的参数声明子句后面不跟mutable

因为副本捕获的实体的行为就像它们是lambda:的成员变量

15[…]对于通过复制捕获的每个实体,在闭包类型中声明一个未命名的非静态数据成员。

而非mutable成员不能在const成员函数([class.this]/1,[dcl.type.cv]/4)内修改,如果要修改捕获的实体,则必须声明mutable lambda。

就目前情况来看,你的lambda看起来是这样的:

class Helper
{
public:
    int operator()(int) const;
private:
    std::map<int, int> memo;
    std::function<int(int)> fn;
};

您可以将mutable lambda视为具有非const operator(),在您的情况下,lambda可以定义如下:

std::function<int(int)> helper = [=](int pos) mutable
// etc

为了使lambda函数成为常量,您需要添加mutable关键字:

std::function<int(int)> Memoize(std::function<int(int)> fn)
    {
        std::map<int, int> memo;
        std::function<int(int)> helper = [=](int pos) mutable // <== HERE!!
        {
            if (memo.count(0) == 0)
            {
                memo[pos] = fn(pos);
            }
            return memo[pos];
        };
        return helper;
    }