如何从元组C ++过滤重复类型

时间:2019-05-01 19:57:24

标签: c++ tuples metaprogramming

一个如何从元组中过滤重复类型?

例如:

using Tuple = std::tuple<int, double, int, double, std::string, std::string>
using FilteredTuple = without_duplicates<Tuple>;

其中的without_duplicates的实现方式是生成以下FilteredTuple类型:

std::tuple<int, double, std::string>

5 个答案:

答案 0 :(得分:5)

这项应有的工作

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]

它通过迭代构造输出元组来工作。在添加每种类型之前,请检查(使用谓词contains)它是否已经存在于输出元组中。如果不是,则添加(std::conditional的“ else”分支),否则不添加(std::conditional的“ then”分支)。

答案 1 :(得分:2)

如果您有权使用Boost,则可以直接使用boost::mp11::mp_unique<your_tuple_type>完成。

例如:

{
  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

答案 2 :(得分:1)

#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

答案 3 :(得分:0)

Piotr的代码非常简洁,应该优先使用。这是适用于C ++ 17(例如std::variant或自定义容器)的常规可变参数模板类的扩展版本:

#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

如果您需要C ++ 11,则只需替换折叠表达式(std::is_same_v<U, Ts> || ...)即可使用自制的disjunction<...>(请参阅cppreference possible implementation)。

答案 4 :(得分:0)

我发现推理“if constexpr”语句通常比在脑海中解析 std::conditional 更容易。

为此,我调整了 Piotr 的答案以适应这种形式: Live Demo

template < typename T, typename ...Rest >
constexpr auto make_unique_tuple( std::tuple< T, Rest... > )
{
    if constexpr ( ( std::is_same_v< T, Rest > || ... ) )
    {
        return make_unique_tuple( std::tuple< Rest... >{} );
    }
    else
    {
        if constexpr ( sizeof...( Rest ) > 0 )
        {
            using remaining = decltype( make_unique_tuple( std::tuple< Rest... >{} ) );
            return std::tuple_cat( std::tuple< T >{}, remaining{} );
        }
        else
        {
            return std::tuple< T >{};
        }
    }
}