我创建了以下元组:
我想知道我应该如何遍历它?确实有tupl_size()
,但是阅读文档时,我不知道如何使用它。我也有搜索,但问题似乎是围绕着Boost::tuple
。
auto some = make_tuple("I am good", 255, 2.1);
发布于 2014-11-13 23:55:58
这里尝试将元组上的迭代分解为组件部分。
首先,表示按顺序执行一系列操作的函数。请注意,许多编译器发现这很难理解,尽管据我所知它是合法的C++11:
template<class... Fs>
void do_in_order( Fs&&... fs ) {
int unused[] = { 0, ( (void)std::forward<Fs>(fs)(), 0 )... }
(void)unused; // blocks warnings
}
接下来,一个函数接受一个std::tuple
,并提取访问每个元素所需的索引。通过这样做,我们可以在以后完善前进。
另外,我的代码还支持std::pair
和std::array
迭代:
template<class T>
constexpr std::make_index_sequence<std::tuple_size<T>::value>
get_indexes( T const& )
{ return {}; }
肉和土豆:
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)) ); }... );
}
和面向公众的界面:
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) );
}
虽然它声明为Tuple
,但它在std::array
s和std::pair
s上工作。它还将所述对象的r/l值类别向下转发到它所调用的函数对象。另请注意,如果您的自定义类型上有一个自由函数get<N>
,并且您覆盖了get_indexes
,则上面的for_each
将在您的自定义类型上工作。
正如前面提到的,许多编译器不支持do_in_order
,因为他们不喜欢将带有未展开的参数包的lambda扩展为参数包。
在这种情况下,我们可以内联do_in_order
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
}
这并不需要太多的冗长,但我个人觉得不太清楚。在我看来,内联操作掩盖了do_in_order
如何工作的影子魔力。
index_sequence
(和支持的模板)是一个可以用C++11编写的C++14特性,在堆栈溢出时很容易找到这样的实现。目前谷歌最热门的是a decent O(lg(n)) depth implementation,如果我没看错的话,它可能是实际的gcc make_integer_sequence
至少一次迭代的基础(这些评论还指出了围绕消除sizeof...
调用的一些进一步的编译时改进)。
或者,我们可以这样写:
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)...};
}
然后:
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))...
);
}
这避免了手动扩展,但可以在更多的编译器上编译。我们通过auto&&i
参数传递Is
。
在C++1z中,我们还可以将std::apply
与for_each_arg
函数对象一起使用,以消除索引摆弄。
发布于 2014-11-13 14:39:54
template<class F, class...Ts, std::size_t...Is>
void for_each_in_tuple(const std::tuple<Ts...> & tuple, F func, std::index_sequence<Is...>){
using expander = int[];
(void)expander { 0, ((void)func(std::get<Is>(tuple)), 0)... };
}
template<class F, class...Ts>
void for_each_in_tuple(const std::tuple<Ts...> & tuple, F func){
for_each_in_tuple(tuple, func, std::make_index_sequence<sizeof...(Ts)>());
}
用法:
auto some = std::make_tuple("I am good", 255, 2.1);
for_each_in_tuple(some, [](const auto &x) { std::cout << x << std::endl; });
Demo。
std::index_sequence
和系列都是C++14的特性,但是它们可以很容易地在C++11中实现(在SO上有很多可用的)。多态lambda也是C++14,但可以用定制编写的functor替换。
https://stackoverflow.com/questions/26902633
复制相似问题