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

时间:2010-01-22 16:04:04

标签: c++ c++11 templates metaprogramming variadic

我想知道C ++ 0x是否提供了任何内置功能来检查可变参数模板的参数包是否包含特定类型。今天,如果你使用boost :: mpl :: vector作为variadic模板的替代品,可以用boost ::: mpl :: contains来实现这个目的。但是,它有严重的编译时间开销。我想,C ++ 0x对std :: is_same有编译器级支持。所以我在想是否也支持 in 编译器中的下面这样的泛化。

template <typename... Args, typename What>
struct is_present
{
  enum { value = (What in Args...)? 1 : 0 };
};

3 个答案:

答案 0 :(得分:3)

不,你必须对可变参数模板使用(部分)特化来进行这样的编译时计算:

#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);
};

你在哪里得到“它有严重的编译时间开销”来自boost mpl?我希望你不只是在这里做出假设。 Boost mpl使用诸如延迟模板实例化之类的技术来尝试减少编译时间而不是像天真模板元编程那样爆炸。

答案 1 :(得分:2)

如果你想避免手动类型递归,std::common_type在我看来是STL中唯一一个可变参数模板的实用程序,因此也是唯一可以封装递归的实用程序。


解决方案1 ​​

std::common_type在一组类型中查找派生最少的类型。如果我们识别具有类型的数字,特别是具有较少派生类型的高数字,则它会在集合中找到最大数字。然后,我们必须将等式映射到键类型到派生级别。

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。标准说

  

如果是,程序可能会专注于此特征   至少有一个模板参数   specialization是用户定义的类型。

并准确描述其中的内容:递归的部分特化情况,应用二元运算符的情况和终端情况。从本质上讲,它是一个通用的fold函数,您可以添加任何二进制操作。在这里我使用了附加因为它比OR更具信息性。请注意,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 > {};

答案 2 :(得分:1)

幸运的是,C ++标准已经得到了发展。使用C ++ 1z或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,被标记为该问题的重复。由于这是该主题的“规范”问题,因此我在此处添加了重要信息。