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

后端 未结 7 1787
时光说笑
时光说笑 2020-11-30 08:50

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 th

相关标签:
7条回答
  • 2020-11-30 08:56

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

    #include <tuple>
    #include <type_traits>
    
    namespace detail {
        struct null { };
    }
    
    template <typename T, typename Tuple>
    struct tuple_contains;
    
    template <typename T, typename... Ts>
    struct tuple_contains<T, std::tuple<Ts...>> :
      std::integral_constant<
        bool,
        !std::is_same<
          std::tuple<typename std::conditional<std::is_same<T, Ts>::value, detail::null, Ts>::type...>,
          std::tuple<Ts...>
        >::value
      >
    { };
    

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

    0 讨论(0)
  • 2020-11-30 08:56

    C++17 and up solution using fold expressions:

    template<typename U, typename... T>
    constexpr bool contains(std::tuple<T...>) {
        return (std::is_same_v<U, T> || ...);
    }
    
    0 讨论(0)
  • 2020-11-30 08:58

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

    #include<type_traits>
    #include<tuple>
    
    template<bool...>
    struct check {};
    
    template<typename U, typename... T>
    constexpr bool contains(std::tuple<T...>) {
        return not std::is_same<
            check<false, std::is_same<U, T>::value...>,
            check<std::is_same<U, T>::value..., false>
        >::value;
    }
    
    int main() {
        static_assert(contains<int>(std::tuple<int, char, double>{}), "!");
        static_assert(contains<char>(std::tuple<int, char, double>{}), "!");
        static_assert(contains<double>(std::tuple<int, char, double>{}), "!");
        static_assert(not contains<float>(std::tuple<int, char, double>{}), "!");
        static_assert(not contains<void>(std::tuple<int, char, double>{}), "!");
    }
    

    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 <utility> header:

    template<typename U, typename... T>
    constexpr auto contains(std::tuple<T...>) {
        return not std::is_same<
            std::integer_sequence<bool, false, std::is_same<U, T>::value...>,
            std::integer_sequence<bool, std::is_same<U, T>::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.

    0 讨论(0)
  • 2020-11-30 08:59
    #include <tuple>
    #include <type_traits>
    
    template <typename T, typename Tuple>
    struct has_type;
    
    template <typename T>
    struct has_type<T, std::tuple<>> : std::false_type {};
    
    template <typename T, typename U, typename... Ts>
    struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {};
    
    template <typename T, typename... Ts>
    struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};
    

    DEMO

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

    template <typename T, typename Tuple>
    using tuple_contains_type = typename has_type<T, Tuple>::type;
    
    0 讨论(0)
  • 2020-11-30 09:06

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

    #include <boost/mpl/unique.hpp>
    #include <boost/mpl/sort.hpp>
    #include <boost/mpl/vector.hpp>
    #include <boost/type_traits/is_same.hpp>
    
    using namespace boost;
    
    template<typename Seq>
    struct unique_concat : 
      mpl::unique<typename mpl::sort<Seq, is_same<mpl::_1,mpl::_2>>::type, 
                  is_same<mpl::_1,mpl::_2>> {};
    
    template<typename T>
    struct print;
    
    int main()
    {
      typedef mpl::vector<int, float, float, char, int, double, int> input;
      print<unique_concat<input>::type> asdf;
    
      return 0;
    }
    
    0 讨论(0)
  • 2020-11-30 09:12

    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 <type_traits>
    #include <tuple>
    
    template <std::size_t... Indices>
    struct index_sequence {
        typedef index_sequence<Indices..., sizeof...(Indices)> next;
    };
    
    template <std::size_t Start>
    struct make_index_sequence {
        typedef typename make_index_sequence<Start - 1>::type::next type;
    };
    
    template <>
    struct make_index_sequence<0> {
        typedef index_sequence<> type;
    };
    
    template <int n>
    using make_index_sequence_t = typename make_index_sequence<n>::type;
    
    template <typename Value, typename Sequence>
    struct lookup;
    
    template <typename Value, std::size_t... index>
    struct lookup<Value, index_sequence<index...>>
    {
    private:
        struct null;
    
        template <typename... Args>
        static std::false_type
        apply(std::conditional_t<std::is_convertible<Args, Value>::value, null, Args>...);
    
        template <typename...>
        static std::true_type apply(...);
    
        template <typename... Args>
        static auto apply_helper(Args&&...) ->
        decltype(apply<std::remove_reference_t<Args>...>(std::declval<Args>()...));
    public:
        template <typename Tuple>
        using value = decltype(
            apply_helper(
                std::declval<
                    typename std::tuple_element<index, Tuple>::type
                >()...
            )
        );
    };
    
    template <typename Value, typename Tuple>
    using has_type = decltype(
        typename lookup<Value,
                        make_index_sequence_t<std::tuple_size<Tuple>::value>
        >::template value<Tuple>{}
    );
    

    Live Demo

    0 讨论(0)
提交回复
热议问题