2016-10-18 48 views
8

Szukam sposobu na porównanie dwóch krotek, aby sprawdzić, czy zawierają te same typy.
Kolejność typów nie ma znaczenia. Dopóki istnieje odwzorowanie jeden do jednego między typami dwóch krotek, uważam je za równoważne.Jak porównać krotki dla równoważnych typów bez względu na kolejność typów?

Oto mały test, który ustawiłem.
Mam problemy wykonawcze equivalent_types():

#include <iostream> 
#include <utility> 
#include <tuple> 
#include <functional> 

template <typename T, typename U> 
bool equivalent_types(T t, U u){ 
    return (std::tuple_size<T>::value == std::tuple_size<U>::value); 
    //&& same types regardless of order 
} 


int main() { 

    //these tuples have the same size and hold the same types. 
    //regardless of the type order, I consider them equivalent. 
    std::tuple<int,float,char,std::string> a; 
    std::tuple<std::string,char,int,float> b; 

    std::cout << equivalent_types(a,b) << '\n'; //should be true 
    std::cout << equivalent_types(b,a) << '\n'; //should be true 

    //examples that do not work: 

    //missing a type (not enough types) 
    std::tuple<std::string,char,int> c; 

    //duplicate type (too many types) 
    std::tuple<std::string,char,int,float,float> d; 

    //wrong type 
    std::tuple<bool,char,int,float> e; 

    std::cout << equivalent_types(a,c) << '\n'; //should be false 
    std::cout << equivalent_types(a,d) << '\n'; //should be false 
    std::cout << equivalent_types(a,e) << '\n'; //should be false 
} 
+0

Zastanawiam się, czy można użyć [this] (http://codereview.stackexchange.com/questions/131194/selection-sorting-a-type-list-compile-time), aby "posortować" typy krotek, a następnie możesz po prostu powtórzyć typy, aby upewnić się, że są tego samego typu. – NathanOliver

+0

Innymi słowy, chcesz uzyskać kompilację 'is_permutation'. –

Odpowiedz

2

Licząc rodzajów zarówno krotki, można zrobić coś takiego:

template <typename T, typename Tuple> 
struct type_counter; 

template <typename T, typename ... Ts> 
struct type_counter<T, std::tuple<Ts...>> : 
    std::integral_constant<std::size_t, (... + std::is_same<T, Ts>::value)> {}; 

template <typename Tuple1, typename Tuple2, std::size_t... Is> 
constexpr bool equivalent_types(const Tuple1&, const Tuple2&, std::index_sequence<Is...>) 
{ 
    return (... 
      && (type_counter<std::tuple_element_t<Is, Tuple1>, Tuple1>::value 
       == type_counter<std::tuple_element_t<Is, Tuple1>, Tuple2>::value)); 
} 

template <typename Tuple1, typename Tuple2> 
constexpr bool equivalent_types(const Tuple1& t1, const Tuple2& t2) 
{ 
    constexpr auto s1 = std::tuple_size<Tuple1>::value; 
    constexpr auto s2 = std::tuple_size<Tuple2>::value; 

    return s1 == s2 
     && equivalent_types(t1, t2, std::make_index_sequence<std::min(s1, s2)>()); 
} 

Demo C++1z
Demo C++14

używam C++ 1z za wyrażenie ale składane można z łatwością przepisać jako funkcję constexpr.

+0

To powinno być '&&', a nie '|'. A kiedy to zrobisz, możesz się pozbyć "prawdziwego". –

+0

@ T.C .: Rzeczywiście, Naprawiono – Jarod42

0

ten kod pojawia się do pracy z parametrami w dowolnej kolejności. Wynik false jest błędem kompilatora. Nie jestem jeszcze świetny z TMP, ale jest to 100% czasu kompilacji .. Chciałbym kilka sugestii, jak to wyczyścić. żywo: https://godbolt.org/g/3RZaMQ

#include <tuple> 
#include <type_traits> 
using namespace std; 

// This struct removes the first instance of TypeToRemove from the Tuple or 'returns' void if it isn't present 
template<class TypeToRemove, class ProcessedTupleParts, class RemainingTuple, class=void> 
struct RemoveType; 

template<class T, class... ProcessedTupleParts, class TupleHead, class... TupleTail> 
struct RemoveType<T, std::tuple<ProcessedTupleParts...>, std::tuple<TupleHead, TupleTail...>, enable_if_t<std::is_same<T, TupleHead>::value>> { 
    using RemovedType = std::tuple<ProcessedTupleParts..., TupleTail...>; 
}; 

template<class T, class... ProcessedTupleParts, class TupleHead, class... TupleTail> 
struct RemoveType<T, std::tuple<ProcessedTupleParts...>, std::tuple<TupleHead, TupleTail...>, enable_if_t<!std::is_same<T, TupleHead>::value>> { 
    using RemovedType = typename RemoveType<T, std::tuple<ProcessedTupleParts..., TupleHead>, std::tuple<TupleTail...>>::RemovedType; 
}; 

template<class T, class... Anything> 
struct RemoveType<T, std::tuple<Anything...>, std::tuple<>> { 
    using RemovedType = void; 
}; 

template<class T1, class T2> 
struct CompareTuples; 

template<class T1Head, class... T1Tail, class T2> 
struct CompareTuples<std::tuple<T1Head, T1Tail...>, T2> { 
    using Result = typename CompareTuples<std::tuple<T1Tail...>, typename RemoveType<T1Head, std::tuple<>, T2>::RemovedType>::Result; 
}; 

template<> 
struct CompareTuples<std::tuple<>, std::tuple<>> { 
    using Result = std::tuple<>; 
}; 


template<class... T2Body> 
struct CompareTuples<std::tuple<>, std::tuple<T2Body...>> { 
    using Result = void; 
}; 

template<class T1> 
struct CompareTuples<T1, void> { 
    using Result = void; 
}; 



int main() { 
    RemoveType<int, std::tuple<>, 
    RemoveType<char, std::tuple<>, std::tuple<int, char>>::RemovedType>::RemovedType aa; 

    CompareTuples<std::tuple<int>, std::tuple<int>>::Result a; 
    CompareTuples<std::tuple<char, int>, std::tuple<int, char>>::Result b; 
    CompareTuples<std::tuple<char, int>, std::tuple<int, char, double>>::Result e; 
    CompareTuples<std::tuple<char, double, int>, std::tuple<int, char, double>>::Result f; 
    CompareTuples<std::tuple<char, double, int>, std::tuple<int, char>>::Result g; 
    CompareTuples<std::tuple<char>, std::tuple<int>>::Result c; 
    CompareTuples<std::tuple<int>, std::tuple<int, char>>::Result d; 

} 
2

Z Hana (pakowane z ostatnich wersji Boost), możemy przekształcić każdy rodzaj krotka na mapie od rodzaju ilości czasu ich wystąpienia, a następnie porównanie tych map do równości:

template <typename T, typename U> 
bool equivalent_types(T t, U u) { 
    namespace hana = boost::hana; 
    auto f = [](auto m, auto&& e) { 
     auto k = hana::decltype_(&e); 
     return hana::insert(hana::erase_key(m, k), 
      hana::make_pair(k, hana::find(m, k).value_or(0) + 1)); 
    }; 
    return hana::fold(t, hana::make_map(), f) == hana::fold(u, hana::make_map(), f); 
} 

Example.

Należy pamiętać, że &e jako argument do hana::decltype_ jest niezbędny do zapewnienia, że ​​np. int i int& są traktowane jako różne typy (zgodnie z przekazem e według odwołania uniwersalnego).

+0

imponujące, ale czytelne, +1 –