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。
int main() {
const int a = 2;
auto f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
不知道它是否应该与 std::function
一起使用,但这肯定有效。
进一步调查:
我创建了一个 class 来尽可能地模仿 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()
重载的对象和捕获变量的字段的行为相同。
如果我们将 class 更改为 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/functions 的参数有关,这是 C++14 的新功能,因此很可能没有成熟的实现。
除非我执行以下任一操作,否则我可以重现此内容:
- 从
a
中删除 const
- 捕获列表
中的名称a
- 将
std::function<void(int)>
更改为 auto
- 通过将
auto b
更改为 int b
使 lambda 非泛型
- 使用 Clang(例如 v3.5.0)
我认为这是一个与优化相关的编译器错误,并且未能在通用 lambda 中检测到 odr-use(尽管有趣的是设置 -O0
有没有效果)。它可能与 bug 61814 有关,但我不认为它 完全 是同一回事,因此:
我已经提出来了 GCC bug 64791.
- (更新:此错误已在 GCC 5.0 中标记为已修复。)
当然,我在 C++14 的措辞中找不到任何明显的禁止您的代码的内容,尽管在新的 C++14 措辞中一般 "obvious" 很少。 :(
[C++14: 5.1.2/6]:
[..] For a generic lambda with no lambda-capture, the closure type has a public non-virtual non-explicit const conversion function template to pointer to function. The conversion function template has the same invented template-parameter-list, and the pointer to function has the same parameter types, as the function call operator template. [..]
[C++14: 5.1.2/12]:
A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration (this excludes any id-expression that has been found to refer to an init-capture's associated non-static data member), is said to implicitly capture the entity (i.e., this
or a variable) if the compound-statement:
- odr-uses (3.2) the entity, or
- names the entity in a potentially-evaluated expression (3.2) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.
[ Example:
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
};
}
—end example ] All such implicitly captured entities shall be declared within the reaching scope of the lambda expression. [ Note: The implicit capture of an entity by a nested lambda-expression can cause its implicit capture by the containing lambda-expression (see below). Implicit odr-uses of this can result in implicit capture. —end note ]
[C++14: 5.1.2/13]:
An entity is captured if it is captured explicitly or implicitly. An entity captured by a lambda-expression is odr-used (3.2) in the scope containing the lambda-expression. [..]
我正在尝试使用 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。
int main() {
const int a = 2;
auto f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
不知道它是否应该与 std::function
一起使用,但这肯定有效。
进一步调查:
我创建了一个 class 来尽可能地模仿 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()
重载的对象和捕获变量的字段的行为相同。
如果我们将 class 更改为 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/functions 的参数有关,这是 C++14 的新功能,因此很可能没有成熟的实现。
除非我执行以下任一操作,否则我可以重现此内容:
- 从
a
中删除 - 捕获列表 中的名称
- 将
std::function<void(int)>
更改为auto
- 通过将
auto b
更改为int b
使 lambda 非泛型
- 使用 Clang(例如 v3.5.0)
const
a
我认为这是一个与优化相关的编译器错误,并且未能在通用 lambda 中检测到 odr-use(尽管有趣的是设置 -O0
有没有效果)。它可能与 bug 61814 有关,但我不认为它 完全 是同一回事,因此:
我已经提出来了 GCC bug 64791.
- (更新:此错误已在 GCC 5.0 中标记为已修复。)
当然,我在 C++14 的措辞中找不到任何明显的禁止您的代码的内容,尽管在新的 C++14 措辞中一般 "obvious" 很少。 :(
[C++14: 5.1.2/6]:
[..] For a generic lambda with no lambda-capture, the closure type has a public non-virtual non-explicit const conversion function template to pointer to function. The conversion function template has the same invented template-parameter-list, and the pointer to function has the same parameter types, as the function call operator template. [..]
[C++14: 5.1.2/12]:
A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration (this excludes any id-expression that has been found to refer to an init-capture's associated non-static data member), is said to implicitly capture the entity (i.e.,this
or a variable) if the compound-statement:
- odr-uses (3.2) the entity, or
- names the entity in a potentially-evaluated expression (3.2) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.
[ Example:
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 }; }
—end example ] All such implicitly captured entities shall be declared within the reaching scope of the lambda expression. [ Note: The implicit capture of an entity by a nested lambda-expression can cause its implicit capture by the containing lambda-expression (see below). Implicit odr-uses of this can result in implicit capture. —end note ]
[C++14: 5.1.2/13]:
An entity is captured if it is captured explicitly or implicitly. An entity captured by a lambda-expression is odr-used (3.2) in the scope containing the lambda-expression. [..]