为什么 [=]{} 有 lambda 捕获?
Why does [=]{} have a lambda capture?
在直观层面上,不需要携带任何状态(通过引用或其他方式)的 lambda 应该可以干净地转换为裸函数指针是有道理的。然而,我最近惊讶地发现 GCC、Clang 和 MSVC 中出现以下失败:
int main(int, char *[]) {
void (*fp)() = []{}; // OK
//fp = [=]{}; // XXX - no user defined conversion operator available
//fp = [&]{}; // XXX - same ...
}
C++17 规范(或至少 visible public draft version N4713)在 § 8.4.5.1 [expr.prim.lambda.closure] 的第 7 项中引用了 lambda有和没有捕获:
The closure type for a non-generic lambda-expression with no lambda-capture whose constraints (if any) are satisfied has a conversion function to pointer to function with C++ language linkage (10.5) having the same parameter and return types as the closure type’s function call operator. ...
但是,查看正式语法,您可以在第 8.4.5 节中看到以下内容 [expr.prim.lambda]:
- lambda-expression :
- lambda-introducer compound-statement
- ...
- lambda-introducer :
- [ lambda-captureopt ]
- ...
和 § 8.4.5.2 [expr.prim.lambda.capture]:
- lambda-capture :
- capture-default
- capture-list
- capture-default, capture-list
- capture-default :
- &
- =
令我沮丧的是,所有的编译器实际上都在遵守法律条文...
为什么语言将捕获的存在定义为声明中狭义的语法区别,而不是基于正文是否包含对任何 non-static/captured 状态的引用?
允许转换的更改是由国家机构评论发起的。见 n3052: Converting Lambdas to Function Pointers which refers to national body comment UK 42:
A lambda with an empty capture list has identical semantics to a regular function type. By requiring this mapping we get an efficient lambda type with a known API that is also compatible with existing operating system and C library functions.
N3052
的决议是:
Resolution: Add a new paragraph: "A lambda expression with an empty capture set shall be convertible to pointer to function type R(P), where R is the return type and P is the parameter-type-list of the lambda expression." Additionally it might be good to (a) allow conversion to function reference and (b) allow extern "C" function pointer types.
...
Add a new paragraph after paragraph 5. The intent of this edit is to obtain a closure-to-function-pointer conversion for a lambda with no lambda-capture.
The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type's function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type's function call operator.
这就是我们今天的处境。请注意评论说 empty capture list 而我们今天所拥有的似乎与评论中的意图相符。
看起来这是一个基于国家机构评论的修复,应用范围很窄。
您提出的规则非常脆弱,尤其是在 P0588R1 之前的世界中,当隐式捕获依赖于 odr-use 时。
考虑:
void g(int);
void h(const int&);
int my_min(int, int);
void f(int i) {
const int x = 1, y = i;
[=]{ g(x); }; // no capture, can convert?
[=]{ g(y); }; // captures y
[=]{ h(x); }; // captures x
[=]{ my_min(x, 0); }; // no capture, can convert?
[=]{ std::min(x, 0); }; // captures x
}
在直观层面上,不需要携带任何状态(通过引用或其他方式)的 lambda 应该可以干净地转换为裸函数指针是有道理的。然而,我最近惊讶地发现 GCC、Clang 和 MSVC 中出现以下失败:
int main(int, char *[]) {
void (*fp)() = []{}; // OK
//fp = [=]{}; // XXX - no user defined conversion operator available
//fp = [&]{}; // XXX - same ...
}
C++17 规范(或至少 visible public draft version N4713)在 § 8.4.5.1 [expr.prim.lambda.closure] 的第 7 项中引用了 lambda有和没有捕获:
The closure type for a non-generic lambda-expression with no lambda-capture whose constraints (if any) are satisfied has a conversion function to pointer to function with C++ language linkage (10.5) having the same parameter and return types as the closure type’s function call operator. ...
但是,查看正式语法,您可以在第 8.4.5 节中看到以下内容 [expr.prim.lambda]:
- lambda-expression :
- lambda-introducer compound-statement
- ...
- lambda-introducer :
- [ lambda-captureopt ]
- ...
和 § 8.4.5.2 [expr.prim.lambda.capture]:
- lambda-capture :
- capture-default
- capture-list
- capture-default, capture-list
- capture-default :
- &
- =
令我沮丧的是,所有的编译器实际上都在遵守法律条文...
为什么语言将捕获的存在定义为声明中狭义的语法区别,而不是基于正文是否包含对任何 non-static/captured 状态的引用?
允许转换的更改是由国家机构评论发起的。见 n3052: Converting Lambdas to Function Pointers which refers to national body comment UK 42:
A lambda with an empty capture list has identical semantics to a regular function type. By requiring this mapping we get an efficient lambda type with a known API that is also compatible with existing operating system and C library functions.
N3052
的决议是:
Resolution: Add a new paragraph: "A lambda expression with an empty capture set shall be convertible to pointer to function type R(P), where R is the return type and P is the parameter-type-list of the lambda expression." Additionally it might be good to (a) allow conversion to function reference and (b) allow extern "C" function pointer types.
...
Add a new paragraph after paragraph 5. The intent of this edit is to obtain a closure-to-function-pointer conversion for a lambda with no lambda-capture.
The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type's function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type's function call operator.
这就是我们今天的处境。请注意评论说 empty capture list 而我们今天所拥有的似乎与评论中的意图相符。
看起来这是一个基于国家机构评论的修复,应用范围很窄。
您提出的规则非常脆弱,尤其是在 P0588R1 之前的世界中,当隐式捕获依赖于 odr-use 时。
考虑:
void g(int);
void h(const int&);
int my_min(int, int);
void f(int i) {
const int x = 1, y = i;
[=]{ g(x); }; // no capture, can convert?
[=]{ g(y); }; // captures y
[=]{ h(x); }; // captures x
[=]{ my_min(x, 0); }; // no capture, can convert?
[=]{ std::min(x, 0); }; // captures x
}