lambda 表达式生成的函数对象的 C++ 属性?

Vai*_*Man 6 c++ lambda attributes language-lawyer

CppCon2022 的这个演讲在 08:40 说:

在 C++23 之前,您可以为lambda 表达式生成的函数对象指定属性。

例如:

auto a = [] () [[deprecated]] { return 42; };
Run Code Online (Sandbox Code Playgroud)

然而,clang 有理由拒绝这段代码:

“已弃用”属性无法应用于类型

而 gcc 和 msvc 很乐意接受它,而不给出使用警告。

https://godbolt.org/z/Wqhq7hP6s

cppreference.com介绍 lambda 表达式的语法为(部分引用):

[捕获] (参数)规格要求(可选) { body }

  • specs - 由specifiersexceptionattrtrailing-return-type按顺序组成;这些组件中的每一个都是可选的
  • attr - 为闭包类型的函数调用运算符或运算符模板的类型 提供属性规范。如此指定的任何属性都不属于函数调用运算符或运算符模板本身,而是属于其类型。(例如,不能使用 [[noreturn]] 属性。)

那么,这是怎么回事?[[deprecated]]lambda 表达式生成的函数对象上的属性的语义是什么 ?

use*_*522 3

这看起来像是基于特定于实现的(不合格)行为的错误。版本 9 之前的 GCC 确实处理了[[deprecated]]该位置的属性,如视频中所述。更高版本警告被[[deprecated]]忽略,目前它根本不警告,这似乎是一种回归,另请参阅此处关于不同属性但相同问题的错误报告。

正如您还引用 cppreference 一样,该属性属于函数调用运算符的类型,请参阅[expr.prim.lambda.closure]/4,并且[[deprecated]]可能不适用于类型,请参阅[dcl.attr.deprecated] /2 . 因此,程序应该是格式错误的,编译器应该提供诊断。

然而,在 C++20 后的草案中,lambda 声明符的decl-specifier-seq中的另一个可能的attribute-specifier-seq仍然存在歧义。这已随CWG 2509删除,但是即使没有删除,它也适用于decl-specifier形式的类型,请参阅[dcl.spec.general]/1,但不能有任何类型lambda 声明符中的说明符。


[[deprecated]]可以位于声明之前或声明器 ID 之后,以应用于正在声明的变量:

[[deprecated]] auto a = [] () { return 42; };
Run Code Online (Sandbox Code Playgroud)

或者

 auto a [[deprecated]] = [] () { return 42; };
Run Code Online (Sandbox Code Playgroud)

在这种情况下,它应该警告任何进一步的使用a(无论是否在通话中),尽管对使用[[deprecated]]实体使用的警告只是建议的做法,而不是严格要求,请参阅[dcl.attr.deprecated]/4

不可能将属性添加到 lambda 的闭包类型,或者通常添加到从 lambda 纯右值创建的对象。在此处的特定情况下,该对象与变量重合,但一般来说,放置一个对象a没有多大意义。[[deprecated]]对象在运行时存在。编译器无法在编译时警告它们的使用。它只能在编译时对变量(和函数等)的使用发出警告。

在 C++23 中,lambda 声明符之前的属性 (before ()) 属于函数调用运算符本身是正确的,因此[[noreturn]]视频中的属性是有意义的。然而,[[deprecated]]如果目的是避免调用 lambda,而不是其他用途,也应该属于那里,例如:

auto a = [] [[deprecated]] () {return 42;};
auto b = a; // no warning
b(); // warning
Run Code Online (Sandbox Code Playgroud)