Search code examples
c++c++20type-traitsnoexceptc++23

Type traits `is_noexcept`, `add_noexcept`, and `remove_noexcept`?


Motivation: In the implementation of P0288 std::move_only_function, I'd like to write a non-allocating special case for conversion from move_only_function<int() noexcept> to move_only_function<int()>:

move_only_function<int() noexcept> f = []() noexcept { return 42; };
move_only_function<int()> g = std::move(f);  // should just copy the bits

I want to write, like,

if constexpr (is_noexcept_version_of<HisSignature, MySignature>::value) { ... }

I wanted to implement that type trait like this:

template<class, class>
struct is_noexcept_version_of : std::false_type {};

template<class Tp>
struct is_noexcept_version_of<Tp noexcept, Tp> : std::true_type {};

but no vendor accepts that; they all think Tp noexcept is a syntax error.

Question: How would you write this kind of type-trait without a combinatorial explosion of partial specializations, i.e. without exhaustively going through all the possible combinations of &, &&, const, etc.? Is it possible to write simple closed-form type traits for is_noexcept_v<T>, add_noexcept_t<T> and remove_noexcept_t<T>?


Solution

  • Aside from qualification conversions, the only possible implicit conversions between pointer-to-function types are the ones that remove noexcept and similarly for pointers-to-member-functions (except for base-to-derived conversion), so I think the following should work

    struct C {};
    
    template<class A, class B>
    struct is_noexcept_version_of : std::bool_constant<
        requires {
           requires std::is_convertible_v<A C::*, B C::*>;
           requires std::is_function_v<A>;
           requires !std::is_same_v<A, B>;
        }> {};