【问题标题】:Checking type of parameter pack using enable_if使用 enable_if 检查参数包的类型
【发布时间】:2015-04-16 10:13:13
【问题描述】:

由于allowed non-type variadic templates 存在限制,我正在尝试使用enable_if 编写一个采用任意数量的双精度数的函数。本质上,我想做这样的事情:

    template<typename... T,
    typename = typename std::enable_if<std::is_convertible<T, double>::value, T>::type>
    foo(T... t){ /* code here */ }

我选择将enable_if 作为未命名参数的默认值,因为我的函数实际上是一个构造函数并且没有返回值。这适用于单个参数,但由于它是可变参数模板,T 是一个参数包,上面的代码无效。那么,如何检查每个参数都可以转换为双精度值呢?

【问题讨论】:

    标签: c++ templates variadic-templates


    【解决方案1】:

    bool_pack 又是诡计了。

    template<bool...> struct bool_pack;
    template<bool... bs> 
    using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
    

    然后

    template<class R, class... Ts>
    using are_all_convertible = all_true<std::is_convertible<Ts, R>::value...>;
    

    最后

    template<typename... T,
    typename = typename enable_if<are_all_convertible<double, T...>::value>::type>
    foo(T... t){ /* code here */}
    

    【讨论】:

    • 简洁,但没有明显概括“任何都是真的”?
    • @KerrekSB "任何都是真的" = 不是 "全是假的"。因此,只需按照类似的方式定义 all_false 并执行 template&lt;bool... bs&gt; using any_true = std::integral_constant&lt;bool, !all_false&lt;bs...&gt;::value&gt;;
    • +1 因为您可以轻松地将 std::is_convertible 更改为任何类型特征,使其成为真正通用的解决方案。
    【解决方案2】:

    您可以在 c++17 中使用 fold expression 来执行与此处发布的其他答案相同的操作,但无需创建模板。

    #include <type_traits>
    
    template <typename... T, typename = 
        typename std::enable_if<
            (true && ... && std::is_convertible_v<T, ___YOUR_TYPE___>),
            void
        >::type
    >
    constexpr auto foo(T...) noexcept {
            // your code 
    }
    

    【讨论】:

      【解决方案3】:

      我认为更简单的是使用std::initializer_list

      foo(std::initializer_list<double> args)
      {
          // Your stuff.
      }
      

      而不是可变参数模板。 除了()之外,它可能还需要使用{}而不是/

      【讨论】:

      • 这使用起来可能很烦人,因为{} 中禁止缩小转换的规则。
      • 我已经有一个 initializer_list&lt;initializer_list&lt;double&gt;&gt; 的构造函数,而为 initializer_list&lt;double&gt; 的情况添加另一个构造函数对于我的界面来说是一个糟糕的选择,因为它有几种不同的行为。
      【解决方案4】:

      这是另一个 () 版本(很大程度上 灵感来自上述 T.C. 的版本):

      #include <type_traits>
      
      template <typename To, typename From, typename... R>
      struct are_all_convertible {
          constexpr static bool value = std::is_convertible<From,To>::value &&
                                        are_all_convertible<To,R...>::value;
      };
      
      template <typename To, typename From>
      struct are_all_convertible<To,From> {
          constexpr static bool value = std::is_convertible<From,To>::value;
      };
      
      template<typename... T,
      typename = typename std::enable_if<are_all_convertible<double, T...>::value>::type>
      foo(T... t){ /* code here */}
      

      【讨论】:

        【解决方案5】:

        这是一种通用方法——用于二进制折叠的 TMP,使用 C++14。首先,让我们定义基本的组合操作:

        #include <type_traits>
        
        struct and_op
        {
            using type = bool;
            using identity = std::true_type;
            template <bool A, bool B> static constexpr bool value = A && B;
        };
        
        struct or_op
        {
            using type = bool;
            using identity = std::false_type;
            template <bool A, bool B> static constexpr bool value = A || B;
        };
        

        现在是真正的fold 技工:

        template <typename Op, typename Op::type...>
        struct fold;
        
        template <typename Op>
        struct fold<Op> : Op::identity {};
        
        template <typename Op, typename Op::type Val>
        struct fold<Op, Val>
            : std::integral_constant<typename Op::type
            , Val> {};
        
        template <typename Op, typename Op::type Val, typename Op::type... Tail>
        struct fold<Op, Val, Tail...>
            : std::integral_constant<typename Op::type
            , Op::template value<Val, fold<Op, Tail...>::value>> {};
        

        接下来,我们需要一种通过绑定从二元特征创建一元特征的方法:

        template <template <typename, typename> class BPred, typename T>
        struct bind_pred
        {
            template <typename U>
            struct pred_1st : std::integral_constant<bool, BPred<T, U>::value> {};
            template <typename U>
            struct pred_2nd : std::integral_constant<bool, BPred<U, T>::value> {};
        };
        

        最后,一个辅助包装器来组合应用一元谓词的结果:

        template <typename Op, template <typename> class UPred, typename ...Args>
        struct fold_pred : fold<Op, UPred<Args>::value...> {};
        

        就是这样。现在让我们开始工作吧:

        template <typename T>
        using maybe_double = bind_pred<std::is_convertible, double>::pred_2nd<T>;
        
        #include <iomanip>
        #include <iostream>
        
        int main()
        {
            std::cout
                << std::boolalpha
                << fold_pred<and_op, maybe_double, int, float>::value << '\n'
                << fold_pred<and_op, maybe_double, int, float, void>::value << '\n';
        }
        

        在 C++17(或 C++1z,更确切地说)中,由于新的折叠表达式,您可以用更少的代码编写直接解决方案。例如:

        template <template <typename> class UPred, typename ...Args>
        static constexpr bool pred_all = (UPred<Args>::value && ...);
        //                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unary fold
        

        用法:

        static_assert(pred_all<maybe_double, int, float>);
        

        【讨论】:

        • 即使在 N4358 中,您仍然可以将 &amp;&amp; 折叠在空包上。
        • @T.C.:哦,对了,&amp;&amp;|| 实际上被保留了下来。我正在删除括号。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-25
        • 1970-01-01
        • 1970-01-01
        • 2020-05-10
        • 2013-10-13
        相关资源
        最近更新 更多