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

How to filter duplicate types from tuple C++

本文关键字:类型 过滤 元组      更新时间:2023-10-16

一个人如何从元组中进行滤波?

例如:

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

在其中实现没有_duplicates的方式以生成以下过滤器类型的方式:

std::tuple<int, double, std::string>
#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

这应该有效:

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示例]

[Godbolt]

它通过迭代构建输出元组来起作用。在添加每种类型之前,请检查(使用谓词contains(是否已经在输出元组中。如果没有,则添加了(否则" std::conditional的分支",否则就不会添加(the'the" the" the' std::conditional的分支(。

如果您可以访问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>);
}

实时示例

我发现,关于'constexpr'语句的推理通常更容易,而不是在我的脑海中划分std ::。

为此,我调整了Piotr的答案以适合这种形式:现场演示

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 >{};
        }
    }
}

piotr的代码非常简洁,应该优先。这里是一般variadic模板类的扩展版本,可用于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...> {};

实时演示

如果您需要C 11,您只需要替换折叠表达式(std::is_same_v<U, Ts> || ...)即可使用自制disjunction<...>(请参阅CPPReference可能实现(。

c 11实现,基于piotr skotnicki和Soilros答案:

#include <type_traits> // for std::false_type, std::conditional
namespace meta {
// Port of std::disjunction from C++14
// Based on: https://en.cppreference.com/w/cpp/types/disjunction
template <typename ... Types>
struct disjunction
        : std::false_type
{};
template <typename B1>
struct disjunction<B1>
        : B1
{};
template <typename B1, typename ... Bn>
struct disjunction<B1, Bn...>
    : std::conditional<
        bool(B1::value),
        B1,
        disjunction<Bn...>
    >::type
{};
} // namespace meta

namespace meta {
/**
    # Example of usage
    @code{.cpp}
    template <typename ... Types>
    using unique_tuple = typename unique<std::tuple<>, Types ...>::type;
    unique_tuple<bool, int, float, int, int, float> tuple; // --> std::tuple<bool, int, float>
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    template <typename ... Types>
    using unique_variant = typename unique<std::variant<>, Types...>::type;
    unique_variant<bool, int, float, int, int, float> var; // --> std::variant<bool, int, float>
    @endcode
*/

/**
    End of recursive call: tuple is forwared using `type`
    Note: In C++20 it may be implemented like:
    @code{.cpp}
    template <typename T, typename ... Types>
    struct unique : std::type_identity<T> {};
    @endcode
*/
template <typename T, typename ... Types>
struct unique {
    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
    Note: In C++17 it may be implemented by using fold-expression:
    @code{.cpp}
    template <
        template<class...> class Tuple, typename... TupleTypes,
        typename U, typename... Us
    >
    struct unique<Tuple<TupleTypes...>, U, Us...>
        : std::conditional<
            (std::is_same_v<U, TupleTypes> || ...)
            , unique_impl<Tuple<TupleTypes...>, Us...>
            , unique_impl<Tuple<TupleTypes..., U>, Us...>>::type
    {};
    @endcode
*/
template <
        template<class...> class Tuple, typename... TupleTypes,
        typename U, typename... Us
>
struct unique< Tuple<TupleTypes...> , U, Us...>
    : std::conditional<
        disjunction< std::is_same<U, TupleTypes> ... >::value
        , unique<Tuple<TupleTypes...>, Us...>
        , unique<Tuple<TupleTypes..., U>, Us...>
     >::type
{};
} // namespace meta

编译时间测试/验证:

#include <tuple>
namespace tests {
using namespace meta;
// No-types check
static_assert(std::is_same<
    unique< std::tuple<> >::type,
    std::tuple<>
>::value == true, "Test failed");
// Single type check
static_assert(std::is_same<
    unique< std::tuple<>, bool >::type,
    std::tuple<bool>
>::value == true, "Test failed");
// No-duplicates check
static_assert(std::is_same<
    unique< std::tuple<>, bool, int, float >::type,
    std::tuple<bool, int, float>
>::value == true, "Test failed");
// Single duplicate check
static_assert(std::is_same<
    unique< std::tuple<>, int, int>::type,
    std::tuple<int>
>::value == true, "Test failed");
// Multiple duplicates check
static_assert(std::is_same<
    unique< std::tuple<>, int, int, int, int, int >::type,
    std::tuple<int>
>::value == true, "Test failed");
// Few duplicates check
static_assert(std::is_same<
    unique< std::tuple<>, int, int,    float, float >::type,
    std::tuple<int, float>
>::value == true, "Test failed");
// Duplicates in the middle check
static_assert(std::is_same<
    unique< std::tuple<>, bool,    int, int, int, float, float, float,    double >::type,
    std::tuple<bool, int, float, double>
>::value == true, "Test failed");
// Duplicates mixed check
static_assert(std::is_same<
    unique< std::tuple<>, bool,    int, int, int,    float,   double, double,    int,    bool, bool>::type,
    std::tuple<bool, int, float, double>
>::value == true, "Test failed");
// Duplicates mixed check
static_assert(std::is_same<
    unique< std::tuple<>, bool, int, float,    bool, int, float >::type,
    std::tuple<bool, int, float>
>::value == true, "Test failed");
// Duplicated type-alias check
static_assert(std::is_same<
    unique< std::tuple<>, short, int, std::int16_t,    std::int32_t >::type,
    std::tuple<short, int>
>::value == true, "Test failed");
// Duplicated type-alias check
static_assert(std::is_same<
    unique< std::tuple<>, signed char, std::int8_t >::type,
    std::tuple<signed char>
>::value == true, "Test failed");
} // namespace tests

[Godbolt]