我有一个i
类型的变量std::size_t
和一个std::tuple
类型的元组。我想得到元组的i
元素。我试过这个:
// bindings... is of type const T&...
auto bindings_tuple = std::make_tuple(bindings...);
auto binding = std::tuple_element<i, const T&...>(bindings_tuple);
但是我得到这个编译错误,说第一个模板参数必须是一个整数常量表达式:
错误:类型为“
std::size_t
”的非类型模板参数(又名“unsigned long
”)不是整数常量表达式
是否有可能获得元组的i
元素,以及如何做到这一点?
如果可能的话,我想在不使用boost的情况下这样做。
答案 0 :(得分:21)
这是可能的:
struct Functor
{
template<typename T>
void operator()(T& t) const { std::cout << t << std::endl; }
};
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_index(int, std::tuple<Tp...> &, FuncT)
{ }
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_index(int index, std::tuple<Tp...>& t, FuncT f)
{
if (index == 0) f(std::get<I>(t));
for_index<I + 1, FuncT, Tp...>(index-1, t, f);
}
auto t = make_tuple(1, 2, "abc", "def", 4.0f);
int i = 2; // for example
for_index(i, t, Functor());
此代码将打印:
ABC
在ideone上运行示例:sample
答案 1 :(得分:16)
你做不到。这不是一个元组的用途。如果您需要动态访问元素,请使用std::array<T,N>
,这与std::tuple<T,...,T>
几乎完全相同,但会为您提供动态[i]
- 运算符;甚至是像std::vector<T>
这样的完全动态的容器。
答案 2 :(得分:13)
这可能不是OP想要的,但无论如何,如果返回变量类型,可以使用运行时 i 返回 i -th元素例如boost::variant
或boost::any
,
#include <tuple>
#include <stdexcept>
#include <boost/variant.hpp>
template <size_t n, typename... T>
boost::variant<T...> dynamic_get_impl(size_t i, const std::tuple<T...>& tpl)
{
if (i == n)
return std::get<n>(tpl);
else if (n == sizeof...(T) - 1)
throw std::out_of_range("Tuple element out of range.");
else
return dynamic_get_impl<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
}
template <typename... T>
boost::variant<T...> dynamic_get(size_t i, const std::tuple<T...>& tpl)
{
return dynamic_get_impl<0>(i, tpl);
}
例如:
#include <string>
#include <iostream>
int main()
{
std::tuple<int, float, std::string, int> tpl {4, 6.6, "hello", 7};
for (size_t i = 0; i < 5; ++ i)
std::cout << i << " = " << dynamic_get(i, tpl) << std::endl;
return 0;
}
将打印:
0 = 4 1 = 6.6 2 = hello 3 = 7 terminate called after throwing an instance of 'std::out_of_range' what(): Tuple element out of range. Aborted
(boost::variant<T...>
需要g ++ 4.7)
答案 3 :(得分:2)
这里的问题是,如果有可能,那么返回类型是什么?它必须在编译时知道,但元组可能包含不同类型的元素。
假设我们有三个元素的元组:
auto tuple = std::make_tuple(10, "", A());
using tuple_type = decltype(tuple);
显然,获得第N个元素并没有多大意义。它是什么类型的?直到运行时才知道它。但是,假设所有元素都支持一些通用协议,而不是获取第N个元素,您可以向其应用函数:
void process(int n)
{
if (n == 0)
func(std::get<0>(tuple));
else if (n == 1)
func(std::get<1>(tuple));
else if (n == 2)
func(std::get<2>(tuple));
}
此代码&#34;动态&#34;在给定索引n
的情况下处理元素。此示例中的通用协议是函数func
,它可以对元组中使用的所有可能类型执行有意义的操作。
然而,手工编写这样的代码是单调乏味的,我们希望使它更通用。让我们从提取应用程序函数开始,这样我们就可以为不同的仿函数重用相同的process
函数:
template<template<typename > class F>
void process(int n)
{
if (n == 0)
{
using E = typename std::tuple_element<0, tuple_type>::type;
F<E>::apply(std::get<0>(tuple));
}
else if (n == 1)
{
using E = typename std::tuple_element<1, tuple_type>::type;
F<E>::apply(std::get<1>(tuple));
}
else if (n == 2)
{
using E = typename std::tuple_element<2, tuple_type>::type;
F<E>::apply(std::get<2>(tuple));
}
}
在这种情况下,F
可以实现为:
// Prints any printable type to the stdout
struct printer
{
static void apply(E e)
{
std::cout << e << std::endl;
}
}
让编译器生成所有代码,让它变得通用:
constexpr static std::size_t arity = std::tuple_size<tuple_type>::value;
template<int N>
struct wrapper
{
template<template<typename, typename ... > class F>
static void apply_to(tuple_type& tuple, int idx)
{
if (idx)
// Double recursion: compile and runtime.
// Compile-time "recursion" will be terminated once
// we reach condition N == tuple arity
// Runtime recursion terminates once idx is zero.
wrapper<N + 1>::template apply_to<F>(tuple, idx - 1);
else
{
// idx == 0 (which means original index is equal to N).
using E = typename std::tuple_element<N, tuple_type>::type;
F<E>::apply(std::get<N>(tuple));
}
}
};
// Termination condition: N == arity.
template<>
struct wrapper<arity>
{
template<template<typename, typename ... > class F>
static void apply_to(tuple_type&, int)
{
// Throw exception or something. Index is too big.
}
};
用法:
wrapper<0>::template apply_to<printer>(tuple, 2);
让它完全通用是另一个故事。至少它需要独立于元组类型。然后,您可能想要生成仿函数的返回类型,因此您可以返回有意义的结果。第三,让functor接受额外的参数。
P.S。我不是真正的C ++开发人员,所以上面的方法可能完全不存在。但是,我发现它对我的微控制器项目非常有用,我希望尽可能在编译时解决这个问题但又足够通用,所以我可以很容易地改变它。例如,&#34;菜单&#34;在我的项目中基本上是一个&#34;动作&#34;元组,每个动作都是一个单独的类,它支持简单的协议,例如&#34;在LCD上的当前位置打印你的标签&#34;和&#34;激活并运行你的UI循环&#34;。