I have a paper that addresses this. Producing a revision is on my todo list; it needs changes to address feedback from the last EWG review and, I think, changes to address the new template parameter kinds added in C++26.
P3324: Attributes for namespace aliases, template parameters, and lambda captures
Tom.
Hi All,
Currently, lambdas can apply attributes to annotate operator() and its type. For example, the following is valid syntax (https://godbolt.org/z/hc4Er3h9h):
[] [[attr]] ([[attr]] int) [[attr]] {}; // ok[] [[attr]] () [[attr]] {}; // ok[] [[attr]] [[attr]] {}; // ok[] [[]] [[]] {}; // ok, empty attribute
But unfortunately, attributes cannot be used in lambda capture lists:
I'm wondering if it would be reasonable to allow it to support attributes?int x = 0;[ [[maybe_unused]] x ](int y) { return y; }; // error
I can think of a practical use case where this is useful in the real world, which is optimizing the size of the lambda itself.
For example, the current standard std::bind_front implementation could be simplified to the following:
However, there is one unsatisfactory aspect here, which is that the members captured by the capture list cannot be optimized for overlap, which is a common optimization in library implementations, i.e., Empty base optimization.template<auto fn, typename... BindArgs>constexpr autobind_front(BindArgs&&... bindargs) -> decltype(auto) {return [...boundargs(std::forward<BindArgs>(bindargs))]<typename Self, typename... CallArgs>(this Self&&, CallArgs&&... callargs) -> decltype(auto) {return std::invoke(fn,std::forward_like<Self>(boundargs)..., std::forward<CallArgs>(callargs)...);};}constexpr int add(int x, int y) { return x + y; }static_assert(bind_front<add>(3)(42) == 45);
If we could use attributes in the capture lists, then we could simply do:
template<auto fn, typename... BindArgs>constexpr autobind_front(BindArgs&&... bindargs) -> decltype(auto) {<typename Self, typename... CallArgs>(this Self&&, CallArgs&&... callargs) -> decltype(auto) {return std::invoke(fn,std::forward_like<Self>(boundargs)..., std::forward<CallArgs>(callargs)...);};}
So that we no longer need to make our own special storage class type to perform such optimization.
I reasonably suspect there may be many more use cases for applying attributes in lambda capture lists.What do you think? Is this a good idea?
Hewill