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

xvw2m8pv  于 2022-12-15  发布在  其他
关注(0)|答案(1)|浏览(154)

CppCon 2022的This talk说在08:40:
在C++23之前,您可以为lambda表达式生成的函数对象指定属性。
例如:

auto a = [] () [[deprecated]] { return 42; };

然而,clang拒绝这段代码是有原因的:
“deprecated”特性不能应用于类型
而gcc和msvc很乐意接受它,而不给出使用警告。
https://godbolt.org/z/Wqhq7hP6s
cppreference.com引入了lambda表达式的语法(部分引用):
[ * 捕获 * ](* 参数 规格 * 需要(可选){ * 正文 * }

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

那么,这里发生了什么呢?lambda表达式生成的函数对象上的[[deprecated]]属性的语义是什么?

ctzwtxfj

ctzwtxfj1#

这看起来像是一个基于特定实现(不一致)行为的错误。版本9之前的GCC确实处理了视频中提到的那个位置的[[deprecated]]属性。后来的版本警告[[deprecated]]被忽略,而目前它根本没有警告,这似乎是一个回归,另请参见bug报告here关于不同属性但相同问题的内容。
由于您还引用了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; };

auto a [[deprecated]] = [] () { return 42; };

在这种情况下,它应该警告任何进一步使用a(无论是否在调用中),尽管警告使用[[deprecated]]实体仅是 * 推荐做法 *,并非严格要求,请参见[dcl.attr.deprecated]/4。
不可能给lambda的闭包类型添加属性,或者一般来说给从lambda右值创建的对象添加属性。在这里的特定情况下,对象与变量a一致。但一般来说,把[[deprecated]]放在一个对象上没有多大意义。对象在运行时存在。编译器不能在编译时警告它们的使用。它只能在编译时警告变量(和函数等)的使用。
在C++23中,lambda声明符之前的属性(()之前)属于函数调用操作符本身是正确的,因此视频中的[[noreturn]]也是有意义的。然而,如果目的是避免调用lambda,而不是其他用途,[[deprecated]]也应该属于那里,例如:

auto a = [] [[deprecated]] () {return 42;};
auto b = a; // no warning
b(); // warning

相关问题