How do I find out if a tuple contains a type?

匿名 (未验证) 提交于 2019-12-03 01:59:02

问题:

Suppose I want to create a compile-time heterogenous container of unique types from some sequence of non-unique types. In order to do this I need to iterate over the source type (some kind of tuple) and check whether each type already exists in my "unique" tuple.

My question is: How can I check whether a tuple (or a boost::fusion container) contains a type?

I'm open to using either the STL or boost.

回答1:

#include  #include   template  struct has_type;  template  struct has_type> : std::false_type {};  template  struct has_type> : has_type> {};  template  struct has_type> : std::true_type {}; 

DEMO

And an additional alias, if the trait itself should be std::true_type or std::false_type :

template  using tuple_contains_type = typename has_type::type; 


回答2:

I actually needed something like this for a project. This was my solution:

#include  #include   namespace detail {     struct null { }; }  template  struct tuple_contains;  template  struct tuple_contains> :   std::integral_constant::value, detail::null, Ts>::type...>,       std::tuple     >::value   > { }; 

The main advantage of this method is that it's one instantiation, no recursion required.



回答3:

Because nobody posted it, I'm adding one more solution based on the bool trick I've learned about here on SO:

#include #include  template struct check {};  template constexpr bool contains(std::tuple) {     return not std::is_same::value...>,         check<:is_same t="">::value..., false>     >::value; }  int main() {     static_assert(contains(std::tuple{}), "!");     static_assert(contains(std::tuple{}), "!");     static_assert(contains(std::tuple{}), "!");     static_assert(not contains(std::tuple{}), "!");     static_assert(not contains(std::tuple{}), "!"); } 

In terms of compile-time performance it's slower than the accepted solution, but it's worth to mention it.


In C++14 it would be even easier to write. The standard template offers already all what you need to do that in the header:

template constexpr auto contains(std::tuple) {     return not std::is_same::value...>,         std::integer_sequence::value..., false>     >::value; } 

This is not far conceptually from what std::get does (available since C++14 for types), but note that the latter fails to compile if the type U is present more than once in T....
If it fits with your requirements mostly depends on the actual problem.



回答4:

Here is a version that does not recursively instantiate the template to check for a matching type. Instead it uses SFINAE with indices-based meta-programming:

#include  #include   template <:size_t... indices=""> struct index_sequence {     typedef index_sequence next; };  template <:size_t start=""> struct make_index_sequence {     typedef typename make_index_sequence::type::next type; };  template  struct make_index_sequence {     typedef index_sequence type; };  template  using make_index_sequence_t = typename make_index_sequence::type;  template  struct lookup;  template  struct lookup> { private:     struct null;      template      static std::false_type     apply(std::conditional_t<:is_convertible value="">::value, null, Args>...);      template      static std::true_type apply(...);      template      static auto apply_helper(Args&&...) ->     decltype(apply<:remove_reference_t>...>(std::declval()...)); public:     template      using value = decltype(         apply_helper(             std::declval::type             >()...         )     ); };  template  using has_type = decltype(     typename lookup::value>     >::template value{} ); 

Live Demo



回答5:

In C++17 you can do it like this:

template  struct has_type;  template  struct has_type> : std::disjunction<:is_same us="">...> {}; 

In C++11 you have to roll your own or / disjunction. Here's a full C++11 version, with tests:

#include  #include   template struct or_ : std::false_type {};  template struct or_ : std::conditional<:value std::true_type="" or_="">>::type {};  /* // C++17 version: template using or_ = std::disjunction; */    template  struct has_type;  template  struct has_type> : or_<:is_same us="">...> {};  // Tests static_assert(has_type>::value == false, "test"); static_assert(has_type>::value == true, "test"); static_assert(has_type>::value == false, "test"); static_assert(has_type>::value == true, "test"); static_assert(has_type>::value == true, "test"); static_assert(has_type>::value == true, "test"); static_assert(has_type>::value == false, "test"); static_assert(has_type>::value == false, "test"); // we're using is_same so cv matters static_assert(has_type>::value == false, "test"); // we're using is_same so cv matters 


回答6:

Since you asked for it, here is a boost::mpl version:

#include  #include  #include  #include   using namespace boost;  template struct unique_concat :    mpl::unique>::type,                is_same<:_1>> {};  template struct print;  int main() {   typedef mpl::vector input;   print::type> asdf;    return 0; } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!