Date: Tue, 23 Sep 2025 18:24:33 +0800
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:
int x = 0;
[ [[maybe_unused]] x ](int y) { return y; }; // error
I'm wondering if it would be reasonable to allow it to support attributes?
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:
template<auto fn, typename... BindArgs>
constexpr auto
bind_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);
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.
If we could use attributes in the capture lists, then we could simply do:
template<auto fn, typename... BindArgs>
constexpr auto
bind_front(BindArgs&&... bindargs) -> decltype(auto) {
return [... [[no_unique_address]]
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)...);
};
}
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
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:
int x = 0;
[ [[maybe_unused]] x ](int y) { return y; }; // error
I'm wondering if it would be reasonable to allow it to support attributes?
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:
template<auto fn, typename... BindArgs>
constexpr auto
bind_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);
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.
If we could use attributes in the capture lists, then we could simply do:
template<auto fn, typename... BindArgs>
constexpr auto
bind_front(BindArgs&&... bindargs) -> decltype(auto) {
return [... [[no_unique_address]]
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)...);
};
}
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
Received on 2025-09-23 10:24:49