价值可变的lambda捕获并不适用于const&?

时间:2015-07-17 21:32:45

标签: c++ c++11 lambda

请考虑以下事项:

void test( const int &value )
{
    auto testConstRefMutableCopy = [value] () mutable {
        value = 2; // compile error: Cannot assign to a variable captured by copy in a non-mutable lambda
    };

    int valueCopy = value;
    auto testCopyMutableCopy = [valueCopy] () mutable {
        valueCopy = 2; // compiles OK
    };
}

为什么第一个版本是编译错误,当我将lambda声明为可变并按值捕获value时(我认为它是它的副本)?

使用clang(x86_64-apple-darwin14.3.0)测试,这是错误消息的来源,以及Visual C ++(vc120)。

2 个答案:

答案 0 :(得分:17)

  

[C++11: 5.1.2/14]: 如果隐式捕获并且 capture-default 为{{1},则实体通过副本 捕获}或如果使用不包含= 的捕获显式捕获它。对于由副本捕获的每个实体,在闭包类型中声明未命名的非静态数据成员。这些成员的声明顺序未指定。 如果实体不是对象的引用,则此类数据成员的类型是相应捕获实体的类型,否则为引用类型。 [..]

lambda中&的类型为value,因为它是通过const int的副本捕获的。

因此,即使lambda的调用操作符函数不是const int&(您标记了lambda const),实际隐式成员mutable的类型为value,但不能被改变了。

坦率地说,这似乎很荒谬;我希望这条规则可以说引用的类型会丢失const int,因为它是副本。 lambda本身上是否存在const关键字(以及生成的调用操作符函数中是否存在mutable关键字)应该是此处唯一的访问控制。

在C ++ 14中,您可以通过捕获const来解决此问题,[value=value]使用与auto相同的规则,从而删除const。 C ++太棒了,不是吗?

答案 1 :(得分:4)

mutable允许lambda修改由copy捕获的非const参数的副本,但不允许它用于const个参数。

因此,此代码有效(并输出inside 2 outside 1):

int a = 1;
[a]() mutable {
    a = 2; // compiles OK
    cout << "inside " << a << "\n";
}();
cout << " outside " << a << "\n";

但如果省略mutable或制作const int,编译器就会出错。

在我们的例子中,第一个lambda会出错,因为valueconst

void test( const int &value )

如果我们copyValue const

const int valueCopy = value;

然后第二个lambda会发生同样的错误。