带有 std::function 的通用 lambda 不会捕获变量
Generic lambda with std::function does not capture variables
我正在尝试使用C++14的通用lambda,但是在std::function上遇到了麻烦。
#include <iostream>
#include <functional>
int main()
{
const int a = 2;
std::function<void(int)> f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
这无法编译,并显示一条错误消息,指出error: ‘a’ was not declared in this scope
.
如果我将其更改为(int b)
,它可以工作。
这是一个错误吗?还是我错过了什么?
我使用的 GCC 版本是 4.9.2。
除非我执行以下任何操作,否则我可以重现它:
- 从
a
中删除const
-
捕获列表中的名称
a
- 将
std::function<void(int)>
更改为auto
- 通过将
auto b
更改为int b
使 lambda 非通用 - 使用 Clang(例如 v3.5.0)
我相信这是一个与优化相关的编译器错误,并且无法在通用 lambda 中检测到 odr 的使用(尽管有趣的是设置-O0
没有效果)。它可能与错误 61814 有关,但我认为它不是一回事,因此:
我已将其作为GCC错误64791提出。
- (更新:此错误已在GCC 5.0中标记为已修复。
当然,我在 C++14 措辞中找不到任何明显的东西应该不允许你的代码,尽管在新的 C++14 措辞中通常很少有"明显"。 :(
[C++14: 5.1.2/6]:
[..]对于没有 lambda 捕获的通用 lambda,闭包类型具有指向函数指针的公共非虚拟非显式 const 函数模板。转换函数模板具有与函数调用运算符模板相同的发明模板参数列表,并且指向函数的指针具有相同的参数类型。[..]:
[C++14: 5.1.2/12]:
具有关联捕获默认值的 lambda 表达式未显式捕获此变量或具有自动存储持续时间的变量(这不包括已发现引用 init-capture 的关联非静态数据成员的任何 id 表达式this
),如果复合语句
- ODR 使用 (3.2) 实体,或
- 在可能计算的表达式 (3.2) 中命名实体,其中封闭的完整表达式依赖于在 lambda 表达式的到达范围内声明的通用 lambda 参数。
[ 示例:
void f(int, const int (&)[2] = {}) { } // #1 void f(const int&, const int (&)[1]) { } // #2 void test() { const int x = 17; auto g = [](auto a) { f(x); // OK: calls #1, does not capture x }; auto g2 = [=](auto a) { int selector[sizeof(a) == 1 ? 1 : 2]{}; f(x, selector); // OK: is a dependent expression, so captures x }; }
—结束示例 ]所有此类隐式捕获的实体都应在 lambda 表达式的到达范围内声明。[ 注意:嵌套 lambda 表达式对实体的隐式捕获可能会导致包含的 lambda 表达式隐式捕获实体(见下文)。对此的隐式 odr 使用可能会导致隐式捕获。—尾注 ]
[C++14: 5.1.2/13]:
如果显式或隐式捕获实体,则会捕获该实体。由 lambda 表达式捕获的实体在包含 lambda 表达式的作用域中使用 (3.2)。[..]
int main() {
const int a = 2;
auto f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
不知道它是否应该与std::function
一起使用,但这肯定有效。
进一步调查:
我创建了一个类来尽可能模仿lambda:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
auto operator()(int b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{};
f2(3); // <- this works
这表明您的示例应该有效。毕竟,所有 lambda 在行为上都与具有operator()
重载的对象和捕获变量的字段相同。
如果我们更改类以进入auto
部分:
这不起作用:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
auto operator()(auto b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{}; // <-- doesn't work
但是,这有效:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
template <class T>
auto operator()(T b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{}; // <-- this works
因此,它很可能与使用 auto
作为 lambda/函数的参数有关,这是 C++14 的新功能,因此很可能没有成熟的实现。
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 在 lambda 捕获中声明的变量的类型推导
- 我可以在这里替换什么,因为我不能在 C# 中使用隐式变量的 lambda 函数?
- 在 C++ 中访问 lambda 捕获初始化变量
- Visual C++: MSVC vs. GCC+CLANG: 处理 lambda 捕获类成员变量,正确的方法是什么?
- 作为模板变量的 Lambda
- 变量不在 lambda 的范围内
- 为什么 lambda 采用变量的初始值
- 为什么捕获 lambda 不捕获变量?
- C++ lambda:如何'freeze'局部变量的值?
- 获取具有静态局部变量的绑定/推断捕获 lambda 的函数指针
- Simulink "Access Violation"写入 C++ lambda 函数捕获列表中的 PWork 变量
- C++使用 lambda 初始化变量
- 如何在 lambda 表达式中传递变量?
- C++将 lambda 函数另存为成员变量,而不使用函数指针进行优化
- C++ lambda - 捕获静态成员变量
- 当使用lambda进行变量的复杂初始化时,如何处理从内部抛出的lambda外部异常
- 无法通过引用捕获 lambda 中的成员变量
- 是否允许在作为静态数据结构成员的lambda函数中捕获变量
- C 递归变量lambda