3

例如,我有一个元组

std::tuple<int, int, int, int> a(2, 3, 1, 4);

我想使用以下函数来获取其元素的位置。

int GetPosition(const std::tuple<int, int, int, int>& tp, int element);

这里2的位置是0,3的位置是1,1的位置是3,4的位置是3。如何实现这个功能?一个愚蠢的方法是

int GetPosition(const std::tuple<int, int, int, int>& tp, int element)
{
    if (std::get<0>(tp) == element) return 0;
    if (std::get<1>(tp) == element) return 1;
    if (std::get<2>(tp) == element) return 2;
    ... // Write as more as an allowed max number of elements
}

还有更好的方法吗?谢谢。

4

3 回答 3

6

更新:

我最终想出了一种以更简单的方式实现这一点的方法,该方法也使用短路(因此执行较少的比较)。

给定一些机器:

namespace detail
{
    template<int I, int N, typename T, typename... Args>
    struct find_index
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<I>(t) == val) ? I :
                find_index<I + 1, N, T, Args...>::call(t, std::forward<T>(val));
        }
    };

    template<int N, typename T, typename... Args>
    struct find_index<N, N, T, Args...>
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<N>(t) == val) ? N : -1;
        }
    };
}

客户将要调用的函数最终归结为这个简单的蹦床:

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
    return detail::find_index<sizeof...(Args), T, Args...>::
           call(t, std::forward<T>(val));
}

最后,这是您在程序中使用它的方式:

#include <iostream>

int main()
{
    std::tuple<int, int, int, int> a(2, 3, 1, 4);
    std::cout << find_index(a, 1) << std::endl; // Prints 2
    std::cout << find_index(a, 2) << std::endl; // Prints 0
    std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}

这是一个活生生的例子


编辑:

如果要进行反向搜索,可以将上述机器和蹦床功能替换为以下版本:

#include <tuple>
#include <algorithm>

namespace detail
{
    template<int I, typename T, typename... Args>
    struct find_index
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<I - 1>(t) == val) ? I - 1 :
                find_index<I - 1, T, Args...>::call(t, std::forward<T>(val));
        }
    };

    template<typename T, typename... Args>
    struct find_index<0, T, Args...>
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<0>(t) == val) ? 0 : -1;
        }
    };
}

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
    return detail::find_index<0, sizeof...(Args) - 1, T, Args...>::
           call(t, std::forward<T>(val));
}

这是一个活生生的例子


原始答案:

这听起来不像是使用元组的典型方式,但如果你真的想这样做,那么这里有一种方法(适用于任何大小的元组)。

首先,一些机器(众所周知的索引技巧):

template <int... Is>
struct index_list { };

namespace detail
{
    template <int MIN, int N, int... Is>
    struct range_builder;

    template <int MIN, int... Is>
    struct range_builder<MIN, MIN, Is...>
    {
        typedef index_list<Is...> type;
    };

    template <int MIN, int N, int... Is>
    struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
    { };
}

template<int MIN, int MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;

然后,几个重载的函数模板:

#include <tuple>
#include <algorithm>

template<typename T, typename... Args, int... Is>
int find_index(std::tuple<Args...> const& t, T&& val, index_list<Is...>)
{
    auto l = {(std::get<Is>(t) == val)...};
    auto i = std::find(begin(l), end(l), true);
    if (i == end(l)) { return -1; }
    else { return i - begin(l); }
}

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
    return find_index(t, std::forward<T>(val), 
                      index_range<0, sizeof...(Args)>());
}

以下是您将如何使用它:

#include <iostream>

int main()
{
    std::tuple<int, int, int, int> a(2, 3, 1, 4);
    std::cout << find_index(a, 1) << std::endl; // Prints 2
    std::cout << find_index(a, 2) << std::endl; // Prints 0
    std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}

这是一个活生生的例子

于 2013-04-08T13:59:19.247 回答
2

比接受的答案略短,向前搜索而不是向后搜索(因此它找到第一个匹配项,而不是最后一个匹配项),并使用constexpr

#include <tuple>

template<std::size_t I, typename Tu>
    using in_range = std::integral_constant<bool, (I < std::tuple_size<Tu>::value)>;

template<std::size_t I1, typename Tu, typename Tv>
constexpr int chk_index(const Tu& t, Tv v, std::false_type)
{
    return -1;
}

template<std::size_t I1, typename Tu, typename Tv>
constexpr int chk_index(const Tu& t, Tv v, std::true_type)
{
    return std::get<I1>(t) == v ? I1 : chk_index<I1+1>(t, v, in_range<I1+1, Tu>());
}

template<typename Tu, typename Tv>
constexpr int GetPosition(const Tu& t, Tv v)
{
    return chk_index<0>(t, v, in_range<0, Tu>());
}
于 2013-04-08T15:06:50.277 回答
0

根据评论修改。通过修改取消的答案的简单方法

template<class Tuple>
struct TupleHelper
{
    TupleHelper(Tuple& _tp) : tp(_tp) {}

    Tuple& tp;

    template<int N>
    int GetPosition(int element)
    {
        if (std::get<N>(tp) == element) return N;
        return GetPosition<N+1>(element);
    }     

    template<>
    int GetPosition<std::tuple_size<Tuple>::value>(int element)
    {
        return -1;
    }  
};    

用它作为

TupleHelper<MyTupleTy>(myTuple).GetPosition<0>(element);

这似乎工作。

于 2013-04-08T15:00:49.900 回答