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