如何在编译时不知道时从 std::tuple 获取第 i 个元素?
How to get the i-th element from an std::tuple when i isn't know at compile-time?
我有一个类型为std::size_t
的变量i
和一个类型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
个元素吗?如何做到这一点?
如果可能的话,我想在不使用助推的情况下完成这项工作
这是可能的:
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
Videone上的工作示例:示例
您不能。这不是元组的作用。如果您需要对元素进行动态访问,请使用std::array<T,N>
,它与std::tuple<T,...,T>
几乎相同,但提供了动态[i]
运算符;或者甚至是像CCD_ 11这样的完全动态容器。
这可能不是OP想要的,但无论如何,只要返回boost::variant
或boost::any
等变体类型,就可以使用运行时i返回第i个元素,
#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=41=6.62=你好3=7在抛出"std::out_of_range"的实例后调用terminatewhat((:元组元素超出范围。中止
(boost::variant<T...>
需要g++4.7(
这里的问题是,如果可能的话,返回类型是什么?它必须在编译时已知,但元组可能包含不同类型的元素。
假设我们有一个由三个元素组成的元组:
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));
}
该代码"动态"处理元素,给定索引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);
不过,让它完全通用是另一回事。至少它需要独立于元组类型。然后,您可能想要生成函子的返回类型,这样就可以返回有意义的结果。第三,使函子接受额外的参数。
附言:我不是真正的C++开发人员,所以上面的方法可能完全没有意义。然而,我发现它对我的微控制器项目很有用,我希望在编译时尽可能多地解决问题,但又足够通用,这样我就可以轻松地处理事情。例如,我的项目中的"菜单"基本上是一个"操作"的元组,每个操作都是一个单独的类,支持简单的协议,如"在LCD上的当前位置打印标签"answers"激活并运行UI循环"。
- C++为构建时间获取QDateTime的可靠方法
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 如何使用 < 和 > 命令获取 c++ 中的输入和输出?
- 使用指针从C++中的数组中获取最大值
- 如何获取std::result_of函数的返回类型
- 如何在openssl-ecc中获取十六进制格式的私钥
- Cpp-Tuple使用带有变量的get
- 使用Unreal C++获取VR耳机的世界位置/方向
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 从C字符串中获取奇怪的字符串长度
- 为什么我的for循环不能正确获取argv
- 从python中调用C++函数并获取返回值
- 如何获取一个数字的前3位
- 获取字符串的长度并将其分配给数组
- 无法获取菜单选择以运行函数.C++
- 如何从对象中获取对元组尾部的引用 std::tuple<Head,Tail...>
- 从"子项"项指针获取"父""std::tuple"
- std::tuple,按继承类型获取项
- 如何在编译时不知道时从 std::tuple 获取第 i 个元素?
- 如何获取对 std::tuple 元素的引用