我最近发现,在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相同。
是否有充分的理由对捕获的类型使用不同的类型推导规则?
答案 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 表达式,这可能是错误的微妙来源。