c++ - Checking noexceptness when applying function to a pack - Stack Overflow

I have the following C++26 function which runs some code for each field of a structure:template <ty

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 badges
Add a comment  | 

2 Answers 2

Reset to default 3

The first thing that comes to mind is making another function to compute the noexceptness, 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条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信