2013-04-08 110 views
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 
} 

任何更好的方法?謝謝。

+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

回答

6

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

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

一個簡單的方法這似乎工作。

+0

這仍然會導致無限遞歸實例化。運行時條件不適用於編譯時遞歸。 – Xeo 2013-04-08 15:08:47

+0

這不起作用。你應該嘗試更復雜的輸入或許 – 2013-04-08 15:14:30

+0

@Xeo:它不會導致無限遞歸,因爲它不是遞歸的 - 它甚至不會自行調用。所以,如果它的工作,那只是偶然 – 2013-04-08 15:15:10

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>()); 
} 
+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