如何获得一个元组元素
例如位置,我有一个元组如何获得一个元组元素
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
}
任何更好的方法?谢谢。
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。
根据意见修改。通过修改取消答案
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);
一个简单的方法这似乎工作。
这仍然会导致无限递归实例化。运行时条件不适用于编译时递归。 – Xeo 2013-04-08 15:08:47
这不起作用。你应该尝试更复杂的输入或许 – 2013-04-08 15:14:30
@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>());
}
好的捕捉,提供in_range谓词作为函数参数! – 2013-04-08 15:09:56
标签调度FTW! :D – 2013-04-08 15:11:08
我已经吸取了教训(我希望) – 2013-04-08 15:16:24
有什么理由你不使用' std :: array'?迭代“std :: tuple”中的元素通常没什么意义,因为它们可以是不同的类型。 –
2013-04-08 13:46:11
我只是使用int(s)简化了我的问题。它们实际上是不同的类型,但都从基类继承而来。 – user1899020 2013-04-08 13:48:25
那么为什么不'std :: array '? :P –
2013-04-08 13:51:31