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:
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./z/EEGxY5zeq
Share Improve this question asked Mar 2 at 14:12 Jean-Michaël CelerierJean-Michaël Celerier 7,9884 gold badges58 silver badges80 bronze badges2 Answers
Reset to default 3The 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.
You can get close with this:
template<class T>
constexpr bool make_is_nothrow_structured_bindable_result() {
void get(); // force the following lookup to be ADL-only
return []<std::size_t... Is>(std::index_sequence<Is...>) {
return noexcept(
(void(get<Is>(std::declval<T>())) , ...)
);
}(std::make_index_sequence<
std::tuple_size_v<std::remove_reference_t<T>>
>());
}
template<class T>
struct is_nothrow_structured_bindable : std::true_type {};
template<class Tp>
requires (std::tuple_size<std::remove_reference_t<Tp>>::value >= 0)
struct is_nothrow_structured_bindable<Tp> :
std::bool_constant<make_is_nothrow_structured_bindable_result<Tp>()> {};
This will tell you whether a structured-bindable type is nothrow-structured-bindable. But it won't tell you whether a type is structured-bindable to begin with; I don't think that's detectable in today's C++ at all.
So we have for example is_nothrow_structured_bindable<int>::value == true
, even though int
is not structured-bindable.
Example usage:
template<bool B>
struct S {
template<int K> friend int get(S) noexcept(B) { return K; }
};
template<bool B> struct std::tuple_size<S<B>> : std::integral_constant<int, 2> {};
template<bool B, int K> struct std::tuple_element<K, S<B>> : std::type_identity<int> {};
static_assert(is_nothrow_structured_bindable<int[2]>::value);
static_assert(is_nothrow_structured_bindable<std::pair<int, int>>::value);
static_assert(is_nothrow_structured_bindable<S<true>>::value);
static_assert(!is_nothrow_structured_bindable<S<false>>::value);
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745120608a4612390.html
评论列表(0条)