This talk of CppCon2022 says at 08:40:
Before C 23, you can specify attributes for the function object generated by a lambda expression.
E.g.:
auto a = [] () [[deprecated]] { return 42; };
However, clang rejects this code with reason:
'deprecated' attribute cannot be applied to type
While gcc and msvc happily accept it WITHOUT giving warning on usage.
https://godbolt.org/z/Wqhq7hP6s
cppreference.com introduces the syntax of lambda expression as (quoted partially):
[ captures ] ( params ) specs requires(optional) { body }
- specs - consists of specifiers, exception, attr and trailing-return-type in that order; each of these components is optional
- attr - provides the attribute specification for the type of the function call operator or operator template of the closure type. Any attribute so specified does not appertain to the function call operator or operator template itself, but its type. (For example, the [[noreturn]] attribute cannot be used.)
So, what's going on here? What is the semantics of the [[deprecated]]
attribute on a function object generated by a lambda expression?
CodePudding user response:
This looks like a mistake based on an implementation-specific (non-conforming) behavior. GCC before version 9 did handle the [[deprecated]]
attribute in that position as stated in the video. Later versions warn that [[deprecated]]
is ignored and currently it does not warn at all, which seems to be a regression, see also the bug report here about a different attribute but the same issue.
As you are also quoting from cppreference, the attribute appertains to the type of the function call operator, see [expr.prim.lambda.closure]/4, and [[deprecated]]
may not be applied to a type, see [dcl.attr.deprecated]/2. Therefore the program should be ill-formed and the compiler should provide a diagnostic.
In the post-C 20 draft there is however still an ambiguity with another possible attribute-specifier-seq in the decl-specifier-seq of the lambda declarator. This was removed with CWG 2509, however even if it wasn't removed, it was meant to apply to the type which the decl-specifier form, see [dcl.spec.general]/1, but there can't be any type specifiers in the lambda declarator.
[[deprecated]]
may be positioned before the declaration or after the declarator-id to apply to the variable that is being declared:
[[deprecated]] auto a = [] () { return 42; };
or
auto a [[deprecated]] = [] () { return 42; };
in which case it should warn on any further use of a
(whether in a call or not), although warning on use of a [[deprecated]]
entity use is only recommended practice, not strictly required, see [dcl.attr.deprecated]/4.
It is not possible to add an attribute to the lambda's closure type or generally to the object which is created from the lambda prvalue. In the specific case here that object coincides with the variable a
, but in general it wouldn't make much sense to put [[deprecated]]
on an object. Objects exists at runtime. The compiler cannot warn about their use at compile-time. It can only warn on the use of variables (and functions, etc.) at compile-time.
In C 23 it is correct that the attribute before the lambda declarator (before ()
) appertains to the function call operator itself, so that [[noreturn]]
there as in the video makes sense. However [[deprecated]]
should also belong there if the intention is to avoid calls to the lambda, but not other use, e.g.:
auto a = [] [[deprecated]] () {return 42;};
auto b = a; // no warning
b(); // warning