如何在 C++ 11 中迭代 std::tuple
我做了以下元组:
我想知道我应该如何迭代它?有 tupl_size()
,但是阅读文档后,我不知道如何使用它.我也有搜索所以,但问题似乎是围绕 Boost::tuple
.
auto some = make_tuple("我很好", 255, 2.1);
解决方案 这里尝试将元组的迭代分解为组件部分.
首先是一个函数,表示按顺序执行一系列操作.请注意,尽管据我所知它是合法的 C++11,但许多编译器发现这很难理解:
template无效do_in_order(Fs&&... fs){int未使用[] = { 0, ( (void)std::forward<Fs>(fs)(), 0 )... }(无效)未使用;//阻止警告}
接下来,一个函数接受 std::tuple
,并提取访问每个元素所需的索引.通过这样做,我们可以在以后完善前进.
作为附带的好处,我的代码支持 std::pair
和 std::array
迭代:
模板constexpr std::make_index_sequence<std::tuple_size<T>::value>get_indexes(T const&){ 返回 {};}
肉和土豆:
template
以及面向公众的界面:
templatevoid for_each(元组&& tup, F&& f) {自动索引 = get_indexes(tup);for_each(索引,std::forward<元组>(tup),std::forward(f));}
当它声明 Tuple
时,它适用于 std::array
s 和 std::pair
s.它还将所述对象的 r/l 值类别向下转发到它调用的函数对象.另请注意,如果您在自定义类型上有一个免费函数 get<N>
,并且您覆盖了 get_indexes
,则上述 for_each
将适用您的自定义类型.
如前所述,许多编译器不支持 do_in_order
,因为它们不喜欢将未扩展参数包扩展为参数包的 lambda.
在这种情况下我们可以内联 do_in_order
template(std::forward(tup)), 0 )... }(无效)未使用;//阻止警告}
这并不需要太多冗长,但我个人觉得不太清楚.在我看来,do_in_order
工作原理的影子魔法被内联的方式掩盖了.
index_sequence
(和支持模板)是可以用 C++11 编写的 C++14 功能.在堆栈溢出上找到这样的实现很容易.当前最热门的谷歌点击是一个不错的 O(lg(n)) 深度实现,如果我正确阅读评论可能会是实际 gcc make_integer_sequence
至少一次迭代的基础(注释还指出了围绕消除 sizeof...
调用的一些进一步的编译时改进).p>
我们也可以这样写:
模板void for_each_arg(F&&f,Args&&...args){使用丢弃=int[];(void)丢弃{0,((void)(f(std::forward<Args>(args))),0)...};}
然后:
template
这避免了手动扩展,但可以在更多编译器上编译.我们通过 auto&&i
参数传递 Is
.
在 C++1z 中,我们还可以使用带有 for_each_arg
函数对象的 std::apply
来消除索引摆弄.
I have made the following tuple:
I want to know how should I iterate over it? There is tupl_size()
, but reading the documentation, I didn't get how to utilize it. Also I have search SO, but questions seem to be around Boost::tuple
.
auto some = make_tuple("I am good", 255, 2.1);
解决方案
Here is an attempt to break down iterating over a tuple into component parts.
First, a function that represents doing a sequence of operations in order. Note that many compilers find this hard to understand, despite it being legal C++11 as far as I can tell:
template<class... Fs>
void do_in_order( Fs&&... fs ) {
int unused[] = { 0, ( (void)std::forward<Fs>(fs)(), 0 )... }
(void)unused; // blocks warnings
}
Next, a function that takes a std::tuple
, and extracts the indexes required to access each element. By doing so, we can perfect forward later on.
As a side benefit, my code supports std::pair
and std::array
iteration:
template<class T>
constexpr std::make_index_sequence<std::tuple_size<T>::value>
get_indexes( T const& )
{ return {}; }
The meat and potatoes:
template<size_t... Is, class Tuple, class F>
void for_each( std::index_sequence<Is...>, Tuple&& tup, F&& f ) {
using std::get;
do_in_order( [&]{ f( get<Is>(std::forward<Tuple>(tup)) ); }... );
}
and the public-facing interface:
template<class Tuple, class F>
void for_each( Tuple&& tup, F&& f ) {
auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f) );
}
while it states Tuple
it works on std::array
s and std::pair
s. It also forward the r/l value category of said object down to the function object it invokes. Also note that if you have a free function get<N>
on your custom type, and you override get_indexes
, the above for_each
will work on your custom type.
As noted, do_in_order
while neat isn't supported by many compilers, as they don't like the lambda with unexpanded parameter packs being expanded into parameter packs.
We can inline do_in_order
in that case
template<size_t... Is, class Tuple, class F>
void for_each( std::index_sequence<Is...>, Tuple&& tup, F&& f ) {
using std::get;
int unused[] = { 0, ( (void)f(get<Is>(std::forward<Tuple>(tup)), 0 )... }
(void)unused; // blocks warnings
}
this doesn't cost much verbosity, but I personally find it less clear. The shadow magic of how do_in_order
works is obscured by doing it inline in my opinion.
index_sequence
(and supporting templates) is a C++14 feature that can be written in C++11. Finding such an implementation on stack overflow is easy. A current top google hit is a decent O(lg(n)) depth implementation, which if I read the comments correctly may be the basis for at least one iteration of the actual gcc make_integer_sequence
(the comments also point out some further compile-time improvements surrounding eliminating sizeof...
calls).
Alternatively we can write:
template<class F, class...Args>
void for_each_arg(F&&f,Args&&...args){
using discard=int[];
(void)discard{0,((void)(
f(std::forward<Args>(args))
),0)...};
}
And then:
template<size_t... Is, class Tuple, class F>
void for_each( std::index_sequence<Is...>, Tuple&& tup, F&& f ) {
using std::get;
for_each_arg(
std::forward<F>(f),
get<Is>(std::forward<Tuple>(tup))...
);
}
Which avoids the manual expand yet compiles on more compilers. We pass the Is
via the auto&&i
parameter.
In C++1z we can also use std::apply
with a for_each_arg
function object to do away with the index fiddling.
相关文章