如何获得一个元组元素

问题描述:

例如位置,我有一个元组如何获得一个元组元素

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 
} 

任何更好的方法?谢谢。

+5

有什么理由你不使用' std :: array '?迭代“std :: tuple”中的元素通常没什么意义,因为它们可以是不同的类型。 – 2013-04-08 13:46:11

+0

我只是使用int(s)简化了我的问题。它们实际上是不同的类型,但都从基类继承而来。 – user1899020 2013-04-08 13:48:25

+1

那么为什么不'std :: array '? :P – 2013-04-08 13:51:31

UPDATE:

我终于想出了一个办法中也使用短路更简单的方式来实现这一目标(并因此少进行比较)。

鉴于一些机械:

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) 
} 

这里是一个live example


编辑:

如果要执行搜索向后,可以代替上述机械与以下版本的蹦床功能:

#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)); 
} 

这里一个live example


原来的答案:

这并没有真正听起来像一个会使用元组典型的方式,但如果你真的要做到这一点,那么这里是一个办法(作品与任何大小的元组)。

首先,一些机械(知名指数招):

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) 
} 

这里是一个live example

+0

不错的例子,但没有可悲的短路评估。 :) – Xeo 2013-04-08 14:09:28

+0

@Xeo:这不难实现 - 我想我们已经做到了,不是吗? ;) – 2013-04-08 14:10:16

+0

是的,我记得那样 - 找不到问题,但。 – Xeo 2013-04-08 14:13:32

根据意见修改。通过修改取消答案

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); 

一个简单的方法这似乎工作。

+0

这仍然会导致无限递归实例化。运行时条件不适用于编译时递归。 – Xeo 2013-04-08 15:08:47

+0

这不起作用。你应该尝试更复杂的输入或许 – 2013-04-08 15:14:30

+0

@Xeo:它不会导致无限递归,因为它不是递归的 - 它甚至不会自行调用。所以,如果它的工作,那只是偶然 – 2013-04-08 15:15:10

比接受的答案略短,并向前搜索不是倒退(所以它找到的第一个比赛,而不是最后一场比赛),并使用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>()); 
} 
+0

好的捕捉,提供in_range谓词作为函数参数! – 2013-04-08 15:09:56

+0

标签调度FTW! :D – 2013-04-08 15:11:08

+0

我已经吸取了教训(我希望) – 2013-04-08 15:16:24