【问题标题】:Detect if a type is a std::tuple?检测类型是否为 std::tuple?
【发布时间】:2012-10-27 14:31:05
【问题描述】:

目前我有两个功能:

template<typename Type> bool f(Type* x);
template<typename... List> bool f(std::tuple<List...>* x);

有什么方法可以将这两个函数与一个额外的模板参数合并,该参数指示传递的类型是否为元组?

template<typename Type, bool IsTuple = /* SOMETHING */> bool f(Type* x);

【问题讨论】:

    标签: c++ templates c++11 tuples typetraits


    【解决方案1】:

    当然,使用is_specialization_of(从here获取并修复链接):

    template<typename Type, bool IsTuple = is_specialization_of<Type, std::tuple>::value>
    bool f(Type* x);
    

    然而,问题是,你真的想要这样吗?通常,如果您需要知道一个类型是否是元组,您需要对元组进行特殊处理,这通常与它的模板参数有关。因此,您可能希望坚持使用重载版本。

    编辑:既然你提到你只需要一小部分专门化,我建议重载,但只适用于小的特殊部分:

    template<class T>
    bool f(T* x){
      // common parts...
      f_special_part(x);
      // common parts...
    }
    

    template<class T>
    void f_special_part(T* x){ /* general case */ }
    
    template<class... Args>
    void f_special_part(std::tuple<Args...>* x){ /* special tuple case */ }
    

    【讨论】:

    • 在正常情况下,这可能是危险的,专业化会更好。但是这个函数是个大函数,里面只有一个很小的if改变元组类型与否。
    • 使用额外的模板参数也意味着可以显式调用f&lt;tuple&lt;...&gt;, false&gt;f&lt;int, true&gt;,这可以通过在函数体中检查is_specialization_of来避免。
    • @Vincent:那么我实际上建议使用标签调度而不是运行时if。 :)
    • static if,我们需要哟哟哟
    • 在 C++ 17 中,可以使用constexpr if
    【解决方案2】:

    对于 C++17,这是一个使用 if constexpr 的相当简单的解决方案

    template <typename> struct is_tuple: std::false_type {};
    
    template <typename ...T> struct is_tuple<std::tuple<T...>>: std::true_type {};
    

    然后你可以这样做:

    template<typename Type> bool f(Type* x) {
        if constexpr (is_tuple<Type>::value) {
            std::cout << "A tuple!!\n";
            return true;
        }
    
        std::cout << "Not a tuple\n";
        return false;
    }
    

    确保其有效的测试:

    f(&some_tuple);
    f(&some_object);
    

    输出:

    一个元组!!
    不是元组


    部分来自answer 的解决方案在此处找到:How to know if a type is a specialization of std::vector?

    【讨论】:

    • 你的第一个 sn-p 使用 SFINAE,这本身就足够了。 if constexpr 没有为解决方案添加任何内容
    【解决方案3】:

    你可以让你的函数服从另一个函数:

    template<typename Type,bool IsTuple> bool f(Type *x);
    
    template<typename Type> 
    inline bool f(Type* x) { return f<Type,false>(x); }
    
    template<typename... List> 
    inline bool f(std::tuple<List...>* x) { return f<std::tuple<List...>,true>(x); }
    

    【讨论】:

      【解决方案4】:

      对于 C++11,这是我的首选模式:

      // IsTuple<T>()
      template <typename T>
      struct IsTupleImpl : std::false_type {};
      
      template <typename... U>
      struct IsTupleImpl<std::tuple <U...>> : std::true_type {};
      
      template <typename T>
      constexpr bool IsTuple() {
        return IsTupleImpl<decay_t<T>>::value;
      }
      

      效果很好。没有依赖项(我不能使用 Boost)。

      【讨论】:

        【解决方案5】:

        可能有点晚了,但您也可以使用更现代的 c++17 样式和模板变量:

        template <typename T>
        constexpr bool IsTuple = false;
        template<typename ... types>
        constexpr bool IsTuple<std::tuple<types...>>   = true;
        

        还有一些测试

        struct TestStruct{};
        
        static_assert(IsTuple<int> == false,                "Doesn't work with literal.");
        static_assert(IsTuple<TestStruct> == false,         "Doesn't work with classes.");
        static_assert(IsTuple<std::tuple<int, char>>,       "Doesn't work with plain tuple.");
        static_assert(IsTuple<std::tuple<int&, char&>>,     "Doesn't work with lvalue references");
        static_assert(IsTuple<std::tuple<int&&, char&&>>,   "Doesn't work with rvalue references");
        

        你可以在这里查看https://godbolt.org/z/FYI1jS

        编辑: 您将需要运行 std::decay、std::remove_volatile、std::remove_const 来处理特殊情况。

        【讨论】:

        • 最简洁的解决方案,因此是最好的解决方案。
        猜你喜欢
        • 1970-01-01
        • 2019-06-15
        • 1970-01-01
        • 2014-08-31
        • 1970-01-01
        • 1970-01-01
        • 2011-06-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多