推导Lambda捕获类型

时间:2019-06-28 18:21:23

标签: c++ c++11

我最近发现,在lambda中按值捕获const对象意味着它在labmda主体内的变量(即lambda的数据成员)也为const
例如:

const int x = 0;
auto foo = [x]{
  // x is const int
};

draft for C++17的第8.1.5.2节中提到了此行为:

  

对于每个通过副本捕获的实体,在闭包类型中声明一个未命名的非静态数据成员。的   这些成员的声明顺序不确定。这种数据成员的类型是引用的类型   如果实体是对对象的引用,则为对引用的函数类型的左值引用(如果实体)   是对函数的引用,是对相应捕获实体的类型的引用,否则对。的成员   匿名联合不得复制。

我希望推导捕获变量的类型与推导auto相同。
是否有充分的理由对捕获的类型使用不同的类型推导规则?

4 个答案:

答案 0 :(得分:7)

在您的示例中,将无法修改x,因为lambda不是mutable,这使函数调用运算符const。但是,即使lambda是mutable,引号也确实在lambda x中成为了const int的类型。

如果我没记错的话,这是C ++ 11中的一个故意设计决定,即在lambda中使用x的行为与在封闭范围内使用x的行为类似。也就是说,

void foo(int&);
void foo(const int&);
const int x = 0;
foo(x);  // calls foo(const int&)
auto foo = [x]() mutable {
    foo(x);  // also calls foo(const int&)
};

这有助于避免在例如某些代码从具有显式循环重写为使用lambda调用标准库算法时发生错误的情况。

如果我对这次回忆不正确,希望有正确答案的人会介入并写下自己的答案。

答案 1 :(得分:1)

不是推理的答案;已经有一个全面的答案here

对于那些想知道如何捕获const变量的非const副本的人,可以使用带有初始化程序的捕获:

const int x = 0;
auto foo = [x = x]() mutable {
    // x is non-const
};

尽管这需要C ++ 14。与C ++ 11兼容的解决方案是将副本复制到lambda之外:

const int x = 0;
int copy = x;
auto foo = [copy]() mutable {
    // copy is non-const
};

答案 2 :(得分:0)

原因是lambda中的operator()默认为const

int main()
{
    const int x = 0;
    auto foo = [x](){}; // main::$_0::operator()() const
    foo();
}

因此,您必须使用mutable lambda:

int main()
{
    const int x = 0;
    auto foo = [x=x](){}; // main::$_0::operator()()
    foo();
}

答案 3 :(得分:0)

<块引用>

对捕获的类型有不同的类型推导规则是否有充分的理由?

这是经过深思熟虑的决定(理由/原因引自下面的 CWG 756),并且部分重写了原始 lambda 提案,

<块引用>

在2009年3月的Summit会议期间,大量关于C++0x的问题 Lambda 是由核心工作组 (CWG) 提出和审查的。明确决定后 对于大多数这些问题,CWG 得出结论,最好重写该部分 在 Lambda 上实现该方向。本文介绍了这种重写。

特别是它的 CWG 756 决议[强调我的]:

<块引用>

[...] 考虑以下示例:

void f() {
  int const N = 10;
  [=]() mutable { N = 30; }  // Okay: this->N has type int, not int const.
  N = 20;  // Error.
}

也就是说,作为闭包对象成员的N不是const, 即使捕获的变量是常量。这看起来很奇怪,因为 捕获基本上是一种捕获本地环境的方法 避免寿命问题的方法。更严重的是,类型的改变 表示 decltype、重载解析和模板的结果 应用于 lambda 中捕获的变量的参数推导 表达式可以与包含该范围的表达式不同 lambda 表达式,这可能是错误的微妙来源。