一个如何从元组中过滤重复类型?
例如:
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>
答案 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;
它通过迭代构造输出元组来工作。在添加每种类型之前,请检查(使用谓词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>);
}
答案 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;
答案 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...> {};
如果您需要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 >{};
}
}
}