代码之家  ›  专栏  ›  技术社区  ›  Sumant

检查C++0x参数包是否包含类型

  •  6
  • Sumant  · 技术社区  · 16 年前

    我想知道C++0x是否提供了任何内置的能力来检查一个变量模板的参数包是否包含一个特定的类型。今天,如果您使用boost::mpl::vector替代variadic模板,则可以使用boost:::mpl::contains来完成此操作。但是,它有严重的编译时间开销。我想,C++ 0x对STD::ISSY也有编译器级支持。所以我在想,如果支持下面这样的推广 在里面 编译器。

    template <typename... Args, typename What>
    struct is_present
    {
      enum { value = (What in Args...)? 1 : 0 };
    };
    
    3 回复  |  直到 7 年前
        1
  •  3
  •   snk_kid    16 年前

    不,您必须对可变模板使用(部分)专门化来进行编译时计算,如下所示:

    #include <type_traits>
    
    template < typename Tp, typename... List >
    struct contains : std::true_type {};
    
    template < typename Tp, typename Head, typename... Rest >
    struct contains<Tp, Head, Rest...>
    : std::conditional< std::is_same<Tp, Head>::value,
        std::true_type,
        contains<Tp, Rest...>
    >::type {};
    
    template < typename Tp >
    struct contains<Tp> : std::false_type {};
    

    变量模板只有一个其他内部操作,这是sizeof运算符的特殊形式,它计算参数列表的长度,例如:

    template < typename... Types >
    struct typelist_len
    {
       const static size_t value = sizeof...(Types);
    };
    

    BoostMPL的“IT有严重的编译时间开销”从何而来?我希望你不只是在这里做假设。BoostMPL使用延迟模板实例化等技术来尝试减少编译时间,而不是像幼稚模板元编程那样爆炸。

        2
  •  2
  •   Potatoswatter    15 年前

    如果要避免手动类型递归, std::common_type 在我看来,这是STL中唯一的一个实用程序,它是一个变量模板,因此也是唯一一个可能封装递归的实用程序。


    解决方案1

    标准::通用类型 在一组类型中查找派生程度最低的类型。如果我们用类型来标识数字,特别是用派生类型较少的高数字,它会在集合中找到最大的数字。然后,我们必须将相等映射到一个派生级别上的键类型。

    using namespace std;
    
    struct base_one { enum { value = 1 }; };
    struct derived_zero : base_one { enum { value = 0 }; };
    
    template< typename A, typename B >
    struct type_equal {
     typedef derived_zero type;
    };
    
    template< typename A >
    struct type_equal< A, A > {
     typedef base_one type;
    };
    
    template< typename Key, typename ... Types >
    struct pack_any {
     enum { value =
         common_type< typename type_equal< Key, Types >::type ... >::type::value };
    };
    


    解决方案2

    我们可以黑客 common_type 再多一点。标准规定

    一个程序可以专门化这个特性,如果 中至少有一个模板参数 专门化是用户定义的类型。

    并准确地描述了它内部的内容:递归部分专用化案例、应用二进制运算符的案例和终端案例。本质上,它是一个通用的 fold 函数,您可以添加任何二进制操作。这里我使用了加法,因为它比或更有信息性。注意 is_same 返回 integral_constant .

    template< typename Addend >
    struct type_sum { // need to define a dummy type to turn common_type into a sum
        typedef Addend type;
    };
    
    namespace std { // allowed to specialize this particular template
    template< typename LHS, typename RHS >
    struct common_type< type_sum< LHS >, type_sum< RHS > > {
        typedef type_sum< integral_constant< int,
         LHS::type::value + RHS::type::value > > type; // <= addition here
    };
    }
    
    template< typename Key, typename ... Types >
    struct pack_count : integral_constant< int,
     common_type< type_sum< is_same< Key, Types > > ... >::type::type::value > {};
    
        3
  •  0
  •   Kai Petzke    7 年前

    幸运的是,C++标准已经发展。使用C++ 1Z AKA C++ 17,您可以在参数包上轻松地迭代。因此,答案的代码(几乎)和问题中建议的一样简单:

    template<typename ... Args, typename What>
    struct is_prsent {
        static constexpr bool value {(std::is_same_v<What, Args> || ...)}; };
    

    奇怪的样子 (std::is_same_v<What, Args> || ...) 由编译器内部扩展到 (std::is_same_v<What, Args[0]> || std::is_same_v<What, Args[1]> || ...) 这正是你想要的。它甚至正确地产生 false 即使是空的 Args 参数包。

    甚至可以在函数或方法中执行整个内联检查-不再需要任何助手结构:

    template<typename T, typename ... List>
    void foo(T t, List ... lst)
    {
        if constexpr((std::is_same_v<T, List> || ...)) {
            std::cout << "T is in List" << std::endl;
        } else {
            std::cout << "T is not in List" << std::endl;
        }
    }
    

    注意:这是从 another question ,标记为此问题的副本。因为这是这个主题的“规范”问题,所以我在这里添加了重要信息。