Reputation: 7936
I have the following C++26 function which runs some code for each field of a structure:
template <typename S, typename F>
constexpr auto for_each_field(S&& s, F&& f)
{
auto&& [... elts] = static_cast<S&&>(s);
((static_cast<F&&>(f)(std::forward_like<S>(elts))), ...);
}
I would like to enable noexcept conditionally, based on whether f can itself throw an exception.
But I am being stopped in my tracks by the fact that at least two expressions are required to check for noexceptness here: one for the destructuring, the second for checking each individual call.
e.g. the "theoretic" code would have to look somehow like
template <typename S, typename F>
constexpr auto for_each_field(S&& s, F&& f) noexcept(noexcept(
auto&& [... elts] = static_cast<S&&>(s);
((static_cast<F&&>(f)(std::forward_like<S>(elts))), ...);
))
{
auto&& [... elts] = static_cast<S&&>(s);
((static_cast<F&&>(f)(std::forward_like<S>(elts))), ...);
}
but that's of course not possible.
Thus, are there any simple options to achieve this in the current state of C++26? Ideally a solution that works with current clang-21 would be great to enable me to test, which can be tried readily from godbolt: https://gcc.godbolt.org/z/EEGxY5zeq
Upvotes: 3
Views: 66
Reputation: 96791
The first thing that comes to mind is making another function to compute the noexcept
ness, which you can encode in the return type:
template <typename S, typename F>
auto is_noexcept_callable(S&& s, F&& f)
{
auto&& [... elts] = static_cast<S&&>(s);
return std::bool_constant<(noexcept(static_cast<F&&>(f)(std::forward_like<S>(elts))) && ...)>{};
}
And then you can pass the result to noexcept(...)
:
template <typename S, typename F>
constexpr auto for_each_field(S&& s, F&& f) noexcept(decltype((is_noexcept_callable)(std::forward<S>(s), std::forward<F>(f))){})
{
// ...
}
As noted by @康桓瑋 in the comments, you also need to separately check the noexcepness of get<I>(...)
, but that's a simple exercise in using std::make_index_sequence
, so I'll leave that as an exercise to the reader.
A structured binding uses get
only if std::tuple_size<T>
is defined, so check for that first. Otherwise the decomposition mechanism can't throw.
Upvotes: 1