【问题标题】:How do I compare tuples for equivalent types disregarding type order?如何在不考虑类型顺序的情况下比较等价类型的元组?
【发布时间】:2017-02-28 07:15:42
【问题描述】:

我正在寻找一种方法来比较两个元组,看看它们是否包含相同的类型。
类型的顺序无关紧要。只要两个元组的类型之间存在一对一的映射,我就会认为它们是等价的。

这是我设置的一个小测试。
我在执行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
}

【问题讨论】:

  • 我想知道您是否可以使用this 对元组类型进行“排序”,然后您可以遍历这些类型以确保它们是相同的类型。
  • 换句话说,你想要一个编译时is_permutation

标签: c++ comparison tuples c++14 stdtuple


【解决方案1】:

通过计算两个元组的类型,您可以执行以下操作:

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++17
Demo C++14

我使用 c++17 进行折叠表达式,但它可以很容易地重写为 constexpr 函数。

【讨论】:

  • 应该是&amp;&amp;,而不是|。一旦你这样做了,你就可以摆脱true
  • @T.C.:确实,已修复
【解决方案2】:

使用 Hana(与最新的 Boost 版本一起打包),我们可以将每个元组类型转换为从类型到它们出现次数的映射,然后比较这些映射是否相等:

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.

请注意,&amp;e 作为 hana::decltype_ 的参数是必要的,以确保例如intint&amp; 被视为不同的类型(同上,通过通用引用传递 e)。

【讨论】:

    【解决方案3】:

    此代码似乎可以按任何顺序处理参数。 false 结果是编译器错误。我对 TMP 还不是很满意,但它是 100% 编译时间。我很想给一些关于如何清理它的建议。 直播: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;
    
    }
    

    【讨论】:

      猜你喜欢
      • 2015-01-08
      • 2013-08-22
      • 1970-01-01
      • 1970-01-01
      • 2018-09-29
      • 2013-11-13
      • 2016-12-25
      • 1970-01-01
      • 2019-09-30
      相关资源
      最近更新 更多