【问题标题】:Type traits `is_noexcept`, `add_noexcept`, and `remove_noexcept`?类型特征 `is noexcept`、`add_ noexcept` 和 `remove noexcept`?
【发布时间】:2022-08-03 03:18:35
【问题描述】:

动机:在P0288std::move_only_function的实现中,我想写一个从move_only_function<int() noexcept>转换为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

我想写,比如,

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

我想像这样实现这种类型特征:

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 {};

但没有供应商接受;他们都认为Tp noexcept 是语法错误。

问题:如果没有部分特化的组合爆炸,即没有穷尽所有可能的组合 &amp;&amp;&amp;const 等,你将如何编写这种类型特征?是否可以为is_noexcept_v&lt;T&gt;add_noexcept_t&lt;T&gt;remove_noexcept_t&lt;T&gt; 编写简单的封闭式类型特征?

  • \"应该只是复制位\" 嗯……这到底是怎么工作的?

标签: c++ c++20 typetraits noexcept c++23


【解决方案1】:

除了限定转换之外,指向函数类型之间唯一可能的隐式转换是那些删除 noexcept 的类型,对于指向成员函数的指针(除了基数到派生的转换),我认为以下应该工作

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>;
    }> {};

【讨论】:

    【解决方案2】:

    你可以通过组合东西来减少组合混乱的长度。

    template<class F>
    struct function_info;
    
    template<class R, class T, class...Args>
    struct function_info<R (T::*)(Args...) const&&>:
      function_helper< R(Args...), class_t<T>, const_v, rvalue_v >
    {};
    // ^^^^^ combinatorial explosion here ^^^^^
    // You have to decompose the type into its independent dimensions, so
    // you get 12 (24 if you want to support both methods and functions
    // transparently). 
    
    template<class Sig, class T, auto constness, auto refness, auto exceptness >
    struct function_helper:
      base_properties<Sig, T, constness, refness, exceptness>
      derived_properties<Sig, T, constness, refness, exceptness>
    {};
    template<class Sig, class T, auto constness, auto refness, auto exceptness >
    using function_helper_t = typename function_helper<Sig, T, consteness, refness, exceptness>::type;
    
    
    template<class Sig, class T, auto constness, auto refness, auto exceptness>
    struct derived_properties {
      using without_const = function_helper_t<Sig, T, no_const_v, refness, exceptness >;
      using with_const = function_helper_t<Sig, T, const_v, refness, exceptness >;
      // ... add/remove each property here.  An add/remove for each dimension.
      // This is the spot that this technique saves on lines
    };
    
    template<class Sig, class T, auto constness, auto refness, exceptness >
    struct func_type;
    // combinatorial explosion here:
    template<class R, class...Args, class T>
    struct func_type<R(Args...), T, const_v, rvalue_v, noexcept_v> {
      using type = R(T::*) const&& noexcept;
    };
    // ^^^^^ You have to rebuild the type from the dimensional values, which
    // means you get 2 * 3 * 2 = 12 different copy-pastas here ^^^
    
    template<class Sig, class T, auto constness, auto refness, auto exceptness >
    struct base_properties:
      func_type<Sig, T, constness, refness, exceptness >
    {
      using sig_t = Sig;
      using class_t = T;
      constexpr auto const_v = constness;
      constexpr auto ref_v = refness;
      constexpr auto except_v = exceptness;
    };
    

    这至少去除了组合爆炸中的一层;也就是说,假设你想对一堆其他类型(noexcept、const、reference ness 等)做同样的事情。

    我们在一个地方分解类型,在另一个地方重新组合它,我们可以重用这些分解/重新组合。

    【讨论】:

      猜你喜欢
      • 2018-07-20
      • 2020-10-06
      • 2016-03-24
      • 2023-03-05
      • 2015-10-17
      • 2018-03-29
      • 2013-03-21
      相关资源
      最近更新 更多