How to filter duplicate types from tuple C++

前端 未结 4 1973
遇见更好的自我
遇见更好的自我 2020-12-17 18:36

How does one filter duplicate types from a tuple?

For example:

using Tuple = std::tuple
usi         


        
相关标签:
4条回答
  • 2020-12-17 18:40
    #include <type_traits>
    #include <tuple>
    
    template <typename T, typename... Ts>
    struct unique : std::type_identity<T> {};
    
    template <typename... Ts, typename U, typename... Us>
    struct unique<std::tuple<Ts...>, U, Us...>
        : std::conditional_t<(std::is_same_v<U, Ts> || ...)
                           , unique<std::tuple<Ts...>, Us...>
                           , unique<std::tuple<Ts..., U>, Us...>> {};
    
    template <typename... Ts>
    using unique_tuple = typename unique<std::tuple<>, Ts...>::type;
    

    DEMO

    0 讨论(0)
  • 2020-12-17 18:41

    Piotr's code is very concise and should be prefered. Here an extended version for general variadic templated classes which works on C++17 (e.g. std::variant or custom containers):

    #include <type_traits>
    
    // end of recursive call: tuple is forwared using `type`
    template <typename T, typename... Ts>
    struct unique_impl {using type = T;};
    
    // recursive call: 1. Consumes the first type of the variadic arguments, 
    //                    if not repeated add it to the tuple.  
    //                 2. Call this again with the rest of arguments
    template <template<class...> class Tuple, typename... Ts, typename U, typename... Us>
    struct unique_impl<Tuple<Ts...>, U, Us...>
        : std::conditional_t<(std::is_same_v<U, Ts> || ...)
                           , unique_impl<Tuple<Ts...>, Us...>
                           , unique_impl<Tuple<Ts..., U>, Us...>> {};
    
    // forward definition
    template <class Tuple>
    struct unique_tuple;
    
    // class specialization so that tuple arguments can be extracted from type
    template <template<class...>class Tuple, typename... Ts>
    struct unique_tuple<Tuple<Ts...>> : public unique_impl<Tuple<>, Ts...> {};
    

    Live Demo

    If you need C++11 you just need to replace the fold expression (std::is_same_v<U, Ts> || ...) to use a self-made disjunction<...> (see cppreference possible implementation).

    0 讨论(0)
  • 2020-12-17 19:01

    This shoudld work:

    template <class Haystack, class Needle>
    struct contains;
    
    template <class Car, class... Cdr, class Needle>
    struct contains<std::tuple<Car, Cdr...>, Needle> : contains<std::tuple<Cdr...>, Needle>
    {};
    
    template <class... Cdr, class Needle>
    struct contains<std::tuple<Needle, Cdr...>, Needle> : std::true_type
    {};
    
    template <class Needle>
    struct contains<std::tuple<>, Needle> : std::false_type
    {};
    
    
    
    template <class Out, class In>
    struct filter;
    
    template <class... Out, class InCar, class... InCdr>
    struct filter<std::tuple<Out...>, std::tuple<InCar, InCdr...>>
    {
      using type = typename std::conditional<
        contains<std::tuple<Out...>, InCar>::value
        , typename filter<std::tuple<Out...>, std::tuple<InCdr...>>::type
        , typename filter<std::tuple<Out..., InCar>, std::tuple<InCdr...>>::type
      >::type;
    };
    
    template <class Out>
    struct filter<Out, std::tuple<>>
    {
      using type = Out;
    };
    
    
    template <class T>
    using without_duplicates = typename filter<std::tuple<>, T>::type;
    

    [Live example]

    It works by iteratively constructing the output tuple. Before each type is added, check (using the predicate contains) whether it's already in the output tuple or not. If not, it's added (the "else" branch of std::conditional), otherwise it's not added (the "then" branch of std::conditional).

    0 讨论(0)
  • 2020-12-17 19:03

    If you have access to Boost, then this can be done directly using boost::mp11::mp_unique<your_tuple_type>.

    For example:

    {
      using not_unique = std::tuple<int, int, char, std::string, double, int>;
      using filtered = boost::mp11::mp_unique<not_unique>;
      static_assert(std::is_same_v<std::tuple<int, char, std::string, double>, filtered>);
    }
    {
      using already_unique = std::tuple<int, char, std::string, double>;
      using filtered = boost::mp11::mp_unique<already_unique>;
      static_assert(std::is_same_v<std::tuple<int, char, std::string, double>, filtered>);
    }
    

    Live example

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