首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在C++ 11中迭代std::tuple

如何在C++ 11中迭代std::tuple
EN

Stack Overflow用户
提问于 2014-11-13 14:27:10
回答 2查看 27.8K关注 0票数 21

我创建了以下元组:

我想知道我应该如何遍历它?确实有tupl_size(),但是阅读文档时,我不知道如何使用它。我也有搜索,但问题似乎是围绕着Boost::tuple

代码语言:javascript
复制
auto some = make_tuple("I am good", 255, 2.1);
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-11-13 23:55:58

这里尝试将元组上的迭代分解为组件部分。

首先,表示按顺序执行一系列操作的函数。请注意,许多编译器发现这很难理解,尽管据我所知它是合法的C++11:

代码语言:javascript
复制
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::pairstd::array迭代:

代码语言:javascript
复制
template<class T>
constexpr std::make_index_sequence<std::tuple_size<T>::value>
get_indexes( T const& )
{ return {}; }

肉和土豆:

代码语言:javascript
复制
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)) ); }... );
}

和面向公众的界面:

代码语言:javascript
复制
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::arrays和std::pairs上工作。它还将所述对象的r/l值类别向下转发到它所调用的函数对象。另请注意,如果您的自定义类型上有一个自由函数get<N>,并且您覆盖了get_indexes,则上面的for_each将在您的自定义类型上工作。

正如前面提到的,许多编译器不支持do_in_order,因为他们不喜欢将带有未展开的参数包的lambda扩展为参数包。

在这种情况下,我们可以内联do_in_order

代码语言:javascript
复制
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...调用的一些进一步的编译时改进)。

或者,我们可以这样写:

代码语言:javascript
复制
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)...};
}

然后:

代码语言:javascript
复制
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::applyfor_each_arg函数对象一起使用,以消除索引摆弄。

票数 26
EN

Stack Overflow用户

发布于 2014-11-13 14:39:54

代码语言:javascript
复制
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)>());
}

用法:

代码语言:javascript
复制
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替换。

票数 29
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26902633

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档