【问题标题】:How can I check type T is among parameter pack Ts...?如何检查类型 T 是否在参数包 Ts 中...?
【发布时间】:2019-06-23 00:00:18
【问题描述】:

如果TTs... 之一,我想编写一个返回true 的函数

template<class T, class... Ts>
bool is_one_of<T, Ts...>();

例如,is_one_of&lt;int, double, int, float&gt; 返回trueis_one_of&lt;int, double, std::string, bool, bool&gt; 返回false

我自己的实现是

template<class T1, class T2>
bool is_one_of<T1, T2>() {
    return std::is_same<T1, T2>;
}

template<class T1, class T2, class... Ts>
bool is_one_of<T1, T2, Ts...>() {
    if (std::is_same<T1, T2>) {
        return true;
    }
    else {
        return is_one_of<T1, Ts...>();
    }
}

这个检查对我来说似乎很常见,所以我想知道标准库中是否已经有这样的功能。

【问题讨论】:

    标签: c++ variadic-templates


    【解决方案1】:

    在您自己的实现中,一个问题是 C++ 不允许对函数模板进行部分特化。

    您可以使用折叠表达式(在 C++17 中引入)代替递归函数调用。

    template<class T1, class... Ts>
    constexpr bool is_one_of() noexcept {
        return (std::is_same_v<T1, Ts> || ...);
    }
    

    如果您使用的是 C++11,其中折叠表达式和 std::disjunction 不可用,您可以像这样实现 is_one_of

    template<class...> struct is_one_of: std::false_type {};
    template<class T1, class T2> struct is_one_of<T1, T2>: std::is_same<T1, T2> {};
    template<class T1, class T2, class... Ts> struct is_one_of<T1, T2, Ts...>: std::conditional<std::is_same<T1, T2>::value, std::is_same<T1, T2>, is_one_of<T1, Ts...>>::type {};
    

    【讨论】:

    • 也可以是一个变量而不是一个函数:)
    【解决方案2】:

    您也可以使用std::disjunction 来避免不必要的模板实例化:

    template <class T0, class... Ts>
    constexpr bool is_one_of = std::disjunction_v<std::is_same<T0, Ts>...>;
    

    找到匹配类型后,剩余的模板不会被实例化。相反,折叠表达式实例化了所有这些。根据您的用例,这可能会显着缩短编译时间。

    【讨论】:

    • 其实OP的实现和std::disjunction的实现很相似
    • @ShaoyuChen 是的。这个想法是“标准库中已经有这样一个函数”,正如 OP 所想的那样
    【解决方案3】:

    检查类型 T 是否在参数包 Ts 中:

    template<class T0, class... Ts>
    constexpr bool is_one_of = (std::is_same<T0, Ts>{}||...);
    

    模板变量。

    替代方案:

    template<class T0, class... Ts>
    constexpr std::integral_constant<bool,(std::is_same<T0, Ts>{}||...)> is_one_of = {};
    

    这有细微的差别。

    【讨论】:

      【解决方案4】:

      其他答案显示了以简洁明了的方式解决此特定问题的几种正确解决方案。这是一个不推荐用于此特定问题的解决方案,但演示了另一种技术:在 constexpr 函数中,您可以使用普通的 for 循环和简单的逻辑,以便在编译时计算结果。这允许摆脱递归和尝试的 OP 代码的部分模板特化。

      #include <initializer_list>
      #include <type_traits>
      
      template<class T, class... Ts>
      constexpr bool is_one_of() {
        bool ret = false;
      
        for(bool is_this_one : {std::is_same<T, Ts>::value...}) {
          ret |= is_this_one;// alternative style: `if(is_this_one) return true;`
        }
      
        return ret;
      }
      
      static_assert(is_one_of<int, double, int, float>(), "");
      static_assert(!is_one_of<int, double, char, bool, bool>(), "");
      

      至少需要 C++14

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-05-01
        • 2022-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-25
        • 2016-03-10
        相关资源
        最近更新 更多