如何检查两个元组的所有成员是否都不同?

7

std::tuple<...>::operator!=,如果两个元组中至少有一个成员不同,则返回true。

我需要一个函数,如果两个比较的元组所有的成员都不同,则返回true:

template <class... Args>
bool areAllMembersDifferent( const std::tuple<Args...>& left, const std::tuple<Args...>& right )
{
    bool allDiff = true;

    // iterate through the tuples are set allDiff to false if one member's is different than other's

    return allDiff;
}

受网络上找到的内容启发,我写了这个代码(改编自一个打印元组内容的函数):

template <std::size_t N, std::size_t, class = make_index_sequence<N>>
struct CheckTupleLoop;

template <std::size_t N, std::size_t J, std::size_t... Is>
struct CheckTupleLoop<N, J, index_sequence<Is...>> {
    template <class Tup>
    int operator()(bool& allDiff, const Tup &left,const Tup &right) {
        if ( std::get<J>(left) == std::get<J>(right) )
            allDiff = false;
        return 0;
    }
};

template <class... Args>
bool areAllMembersDifferent( const std::tuple<Args...>& left, const std::tuple<Args...>& right )
{
    bool allDiff = true;
    CheckTupleLoop<sizeof...(Args)>{}(allDiff,left,right);
    return allDiff;
}

但这显然是不正确的,因为编译器报告我Error C2955 'CheckTupleLoop': use of class template requires template argument list

在C++11中,任何一种实现bool areAllMembersDifferent的方法都是可接受的(使用或不使用我的第一次尝试方法)。


你是想检查两个元组的内容吗?我被“如果一个元组的所有成员都不同,则会返回true”这句话弄糊涂了。 - Jonas
@Jonas:检查两个元组的所有成员是否都不同。已编辑帖子。 - jpo38
Boost.Hana有一个disjoint函数,用于检查元组是否具有共同的元素。 - TemplateRex
3个回答

5
您可以使用以下内容:
namespace detail
{

template <std::size_t ... Is, typename Tuple>
bool areAllMembersDifferent(std::index_sequence<Is...>,
                            const Tuple& left,
                            const Tuple& right)
{
    bool res = true;

    const int dummy[] = {0, (res &= std::get<Is>(left) != std::get<Is>(right), 0)...};
    static_cast<void>(dummy); // Avoid warning for unused variable
    return res;
}

}

template <typename Tuple>
bool areAllMembersDifferent(const Tuple&left, const Tuple& right)
{
    return detail::areAllMembersDifferent(
        std::make_index_sequence<std::tuple_size<Tuple>::value>(), left, right);
}

演示

实现c++11的std::make_index_sequence(C++14中的函数)可以很容易地找到。

在C++17中,您甚至可以简化助手函数:

namespace detail
{

template <std::size_t ... Is, typename Tuple>
bool areAllMembersDifferent(std::index_sequence<Is...>,
                            const Tuple& left,
                            const Tuple& right)
{
    return (std::get<Is>(left) != std::get<Is>(right) && ...);
}

}

非常好用!谢谢。希望有一天我能够不问SO就能写出这种代码....;-) - jpo38

2
您可以使用以下符合C++11标准的解决方案来实现您所需的功能:
template <size_t N>
struct CompareTuples
{
    template<class... Args>
    static bool areAllMembersDifferent(const std::tuple<Args...>& left, const std::tuple<Args...>& right)
    {
        return (std::get<N>(left) != std::get<N>(right)) && CompareTuples<N-1>::areAllMembersDifferent(left, right);
    }
};

template<>
struct CompareTuples<0>
{
    template<class... Args>
    static bool areAllMembersDifferent(const std::tuple<Args...>& left, const std::tuple<Args...>& right)
    {
        return (std::get<0>(left) != std::get<0>(right));
    }
};

template<class... Args>
bool areAllMembersDifferent(const std::tuple<Args...>& left, const std::tuple<Args...>& right)
{
    return CompareTuples<std::tuple_size<std::tuple<Args...>>::value-1>::areAllMembersDifferent(left, right);
}

0
Jarod42的回答很合理,但这是我的个人观点:
#include <iostream>
#include <tuple>
#include <limits>

template <size_t index>
struct next_index
{
    static const size_t value = index - 1;
};

template <>
struct next_index<0>
{
    static const size_t value = 0;
};

template <class Tuple, size_t index>
bool is_same(const Tuple& left, const Tuple& right)
{
    if (index != 0)
        return is_same<Tuple, next_index<index>::value>(left, right) and std::get<index>(left) != std::get<index>(right);
    return std::get<index>(left) != std::get<index>(right);
}

template <typename Tuple>
bool areAllMembersDifferent(const Tuple& left, const Tuple& right)
{
    return is_same<Tuple, std::tuple_size<Tuple>::value - 1>(left, right);
}

int main() {
    std::cout << areAllMembersDifferent(std::make_tuple(12, '*', 4.2f), std::make_tuple(11, '#', 4.25f)) << std::endl;
    std::cout << areAllMembersDifferent(std::make_tuple(12, '*', 4.2f), std::make_tuple(11, '#', 4.2f)) << std::endl;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接