While writing a cpp library I ended up with possiblity that std::variants shall contain repeating types, that is for example, std::variant<int, double, int, std::string, int>. It may be useful in some occasions but in case of my library I find this unnecessary. And so I would like to filter type reptitions like this:

std::variant<int, double, int, std::string, int> -> std::variant<int, double, std::string>

std::variant<std::string, std::string, int> -> std::variant<std::string, int>

How can this be done?

3

Best Answer


#include <type_traits>#include <variant>template <typename T, typename... Ts>struct filter_duplicates { using type = T; };template <template <typename...> class C, typename... Ts, typename U, typename... Us>struct filter_duplicates<C<Ts...>, U, Us...>: std::conditional_t<(std::is_same_v<U, Ts> || ...), filter_duplicates<C<Ts...>, Us...>, filter_duplicates<C<Ts..., U>, Us...>> {};template <typename T>struct unique_variant;template <typename... Ts>struct unique_variant<std::variant<Ts...>> : filter_duplicates<std::variant<>, Ts...> {};template <typename T>using unique_variant_t = typename unique_variant<T>::type;

DEMO

With Boost.Mp11, this is a short one-liner (as always):

using V1 = mp_unique<std::variant<int, double, int, std::string, int>>;// V1 is std::variant<int, double, std::string>using V2 = mp_unique<std::variant<std::string, std::string, int>>;// V2 is std::variant<std::string, int>

My own version using C++11 (non-boost):

#include <type_traits>template <typename T, typename... Args>struct contains : std::integral_constant<bool, false> {};template <typename T, typename T2, typename... Args>struct contains<T, T2, Args...> : contains<T, Args...> {};template <typename T, typename... Args>struct contains<T, T, Args...> : std::integral_constant<bool, true> {};template <typename W, typename... Ts>struct __make_unique_impl {using type = W;};template <template <typename...> class W, typename... Current, typename T,typename... Ts>struct __make_unique_impl<W<Current...>, T, Ts...>: std::conditional<contains<T, Ts...>::value,typename __make_unique_impl<W<Current...>, Ts...>::type,typename __make_unique_impl<W<Current..., T>, Ts...>::type> {};template <typename W>struct make_unique;template <template <typename...> class W, typename... T>struct make_unique<W<T...>> : __make_unique_impl<W<>, T...> {};

How to use:

#include <variant>using test_t = typename make_unique<std::variant<int, std::string, std::string, int>>::type; // std::variant<int, std::string>