如何迭代 std::tuple 的元素?
如何迭代元组(使用 C++11)? 我尝试了以下操作:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
但这不起作用:
错误 1:抱歉,未实现:无法将“Listener ...”扩展为固定长度的参数列表。
错误2:i不能出现在常量表达式中。
那么,如何正确迭代元组的元素呢?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(23)
为此,C++ 引入了扩展语句。 他们原本有望进入 C++20,但由于缺乏时间进行语言措辞审查而险些被淘汰(参见 此处和此处)。
当前商定的语法(请参阅上面的链接)是:
C++ is introducing expansion statements for this purpose. They were originally on track for C++20 but narrowly missed the cut due to a lack of time for language wording review (see here and here).
The currently agreed syntax (see the links above) is:
Boost.Fusion 是一种可能性:
未经测试的示例:
Boost.Fusion is a possibility:
Untested example:
在 C++17 中,你可以这样做:
这已经在 Clang++ 3.9 中使用 std::experimental::apply 工作。
In C++17 you can do this:
This already works in Clang++ 3.9, using std::experimental::apply.
在 C++17 中,使用 < 可以更简单、直观且编译器友好地执行此操作code>if constexpr:
这是编译时递归,类似于@emsr 提出的递归。 但这不使用 SFINAE,所以(我认为)它对编译器更友好。
A more simple, intuitive and compiler-friendly way of doing this in C++17, using
if constexpr
:This is compile-time recursion, similar to the one presented by @emsr. But this doesn't use SFINAE so (I think) it is more compiler-friendly.
使用 Boost.Hana 和通用 lambda:
http://coliru.stacked-crooked.com/a/27b3691f55caf271
Use Boost.Hana and generic lambdas:
http://coliru.stacked-crooked.com/a/27b3691f55caf271
这是仅使用标准库迭代元组项的简单 C++17 方法:
示例:
输出:
这可以扩展为有条件地中断循环,以防可调用返回值(但仍然适用于不返回 bool 的可调用可分配的值,例如 void):
示例:
输出:
Here's an easy C++17 way of iterating over tuple items with just standard library:
Example:
Output:
This can be extended to conditionally break the loop in case the callable returns a value (but still work with callables that do not return a bool assignable value, e.g. void):
Example:
Output:
您需要使用模板元编程,此处使用 Boost.Tuple 进行展示:
在 C++0x 中,您可以将
print_tuple()
编写为可变参数模板函数。You need to use template metaprogramming, here shown with Boost.Tuple:
In C++0x, you can write
print_tuple()
as a variadic template function instead.另一种选择是为元组实现迭代器。 这样做的优点是您可以使用标准库提供的各种算法和基于范围的 for 循环。 这里解释了一种优雅的方法 https://foonathan.net/2017/03/tuple-迭代器/。 基本思想是使用
begin()
和end()
方法将元组转换为范围以提供迭代器。 迭代器本身返回一个std::variant<...>
,然后可以使用std::visit
访问它。这里有一些例子:
我的实现(很大程度上基于上面链接中的解释):
通过将
const std::tuple<>&
传递给也支持只读访问>to_range()
。Another option would be to implement iterators for tuples. This has the advantage that you can use a variety of algorithms provided by the standard library and range-based for loops. An elegant approach to this is explained here https://foonathan.net/2017/03/tuple-iterator/. The basic idea is to turn tuples into a range with
begin()
andend()
methods to provide iterators. The iterator itself returns astd::variant<...>
which can then be visited usingstd::visit
.Here some examples:
My implementation (which is heavily based on the explanations in the link above):
Read-only access is also supported by passing a
const std::tuple<>&
toto_range()
.如果你想使用 std::tuple 并且你有支持可变参数模板的 C++ 编译器,请尝试下面的代码(使用 g++4.5 测试)。 这应该是你问题的答案。
boost::fusion 是另一种选择,但它需要自己的元组类型:boost::fusion::tuple。 让我们更好地遵守标准! 这是一个测试:
可变参数模板的威力!
If you want to use std::tuple and you have C++ compiler which supports variadic templates, try code bellow (tested with g++4.5). This should be the answer to your question.
boost::fusion is another option, but it requires its own tuple type: boost::fusion::tuple. Lets better stick to the standard! Here is a test:
the power of variadic templates!
在 MSVC STL 中,有一个 _For_each_tuple_element 函数(未记录):
In MSVC STL there's a _For_each_tuple_element function (not documented):
使用
constexpr
和if constexpr
(C++17) 这相当简单直接:Using
constexpr
andif constexpr
(C++17) this is fairly simple and straight forward:其他人提到了一些设计良好的第三方库,您可以参考。 但是,如果您在没有这些第三方库的情况下使用 C++,以下代码可能会有所帮助。
注意:该代码可以使用任何支持 C++11 的编译器进行编译,并且与标准库的设计保持一致:
元组不必是
std::tuple
,而可以是任何内容支持 std::get 和 std::tuple_size ; 特别是,可以使用std::array
和std::pair
;元组可以是引用类型或 cv 限定的;
它具有与
std::for_each
类似的行为,并返回输入UnaryFunction
;对于 C++14(或最新版本)用户,
typename std::enable_if::type
和typename std::decay::type 可以替换为其简化版本,
std::enable_if_t
和std::decay_t
;对于 C++17(或更新版本)用户,
std::tuple_size::value
可以替换为其简化版本std::tuple_size_v
。对于 C++20(或更高版本)用户,
SFINAE
功能可以通过Concepts
实现。Others have mentioned some well-designed third-party libraries that you may turn to. However, if you are using C++ without those third-party libraries, the following code may help.
Note: The code compiles with any compiler supporing C++11, and it keeps consistency with design of the standard library:
The tuple need not be
std::tuple
, and instead may be anything that supportsstd::get
andstd::tuple_size
; in particular,std::array
andstd::pair
may be used;The tuple may be a reference type or cv-qualified;
It has similar behavior as
std::for_each
, and returns the inputUnaryFunction
;For C++14 (or laster version) users,
typename std::enable_if<T>::type
andtypename std::decay<T>::type
could be replaced with their simplified version,std::enable_if_t<T>
andstd::decay_t<T>
;For C++17 (or laster version) users,
std::tuple_size<T>::value
could be replaced with its simplified version,std::tuple_size_v<T>
.For C++20 (or laster version) users, the
SFINAE
feature could be implemented with theConcepts
.我可能错过了这趟火车,但这将在这里供将来参考。
这是我基于此答案和此要点:
然后按如下方式使用它:
可能还有改进的空间。
根据OP的代码,它会变成这样:
I might have missed this train, but this will be here for future reference.
Here's my construct based on this answer and on this gist:
You then use it as follow:
There could be room for improvements.
As per OP's code, it would become this:
在我在这里看到的所有答案中,这里 和这里,我喜欢@sigidagi 的最佳迭代方式。 不幸的是,他的回答非常冗长,在我看来,这掩盖了固有的清晰度。
这是他的解决方案的我的版本,它更简洁,并且适用于 std::tuple、std::pair 和 std::array。
演示:coliru
C++14 的
std::make_index_sequence
可以针对 C++11 实现。Of all the answers I've seen here, here and here, I liked @sigidagi's way of iterating best. Unfortunately, his answer is very verbose which in my opinion obscures the inherent clarity.
This is my version of his solution which is more concise and works with
std::tuple
,std::pair
andstd::array
.Demo: coliru
C++14's
std::make_index_sequence
can be implemented for C++11.扩展 @Stypox 答案,我们可以使他们的解决方案更加通用(C++17 以后)。 通过添加可调用函数参数:
然后,我们需要一个策略来访问每种类型。
让我们从一些助手开始(前两个来自 cppreference):
variant_ref 用于允许修改元组的状态。
用法:
结果:
为了完整起见,这里是我的
Bar
& Foo:Expanding on @Stypox answer, we can make their solution more generic (C++17 onward). By adding a callable function argument:
Then, we need a strategy to visit each type.
Let start with some helpers (first two taken from cppreference):
variant_ref
is used to allow tuples' state to be modified.Usage:
Result:
For completeness, here are my
Bar
&Foo
:我在迭代函数对象元组时偶然发现了同样的问题,所以这里还有一个解决方案:
输出:
I have stumbled on the same problem for iterating over a tuple of function objects, so here is one more solution:
Output:
有很多很好的答案,但由于某种原因,大多数答案都不会考虑返回将 f 应用于我们的元组的结果......
还是我忽略了它? 无论如何,这里还有另一种方法可以做到这一点:
用风格进行 Foreach(有争议)
并从中返回:
实现(非常简单)
编辑:它变得有点混乱。
我不会在这里包含一些元编程样板,因为它肯定会降低可读性,而且我相信这些已经在 stackoverflow 上的某个地方得到了回答。
如果您感到懒惰,请随时查看我的 github 存储库 来实现这两个功能
这也是 std::moving 对象成员的一个示例,为此我最好参考这篇不错的简介 文章
PS 如果您卡住检查是否所有“decltype( std:: move(op).f(std::declval()) )..." 类型无效
你可以找到一些元编程库,或者,如果这些库看起来太难掌握(其中一些可能是由于一些疯狂的元编程技巧),你知道在哪里看看
There're many great answers, but for some reason most of them don't consider returning the results of applying f to our tuple...
or did I overlook it? Anyway, here's yet another way you can do that:
Doing Foreach with style (debatable)
And returning from that:
Implementation (pretty simple one)
Edit: it gets a little messier.
I won't include some metaprogramming boilerplate here, for it will definitely make things less readable and besides, I believe those have already been answered somewhere on stackoverflow.
In case you're feeling lazy, feel free to peek into my github repo for implementation of both
This is also an example of std::moving object's members, for which I'll better refer to this nice brief article
P.S. If you're stuck checking if all "decltype( std::move(op).f(std::declval()) )..." types are void
you can find some metaprogramming library, or, if those libraries seem too hard to grasp (which some of them may be due to some crazy metaprogramming tricks), you know where to look
这是一个基于 std::interger_sequence 的解决方案。
因为我不知道
my_tuple
是否是从代码中的std::make_tuple(T &&...)
构造的。 这对于如何在下面的解决方案中构造std::integer_sequence
至关重要。(1) 如果你的函数之外已经有一个
my_tuple
(不使用template
),如果你还没有,你可以使用 (2)在您的函数中构造了
my_tuple
并希望处理您的T ...arguments
Here is a solution based on
std::interger_sequence
.As I don't know if
my_tuple
is constructed fromstd::make_tuple<T>(T &&...)
in your code. It's essential for how to constructstd::integer_sequence
in the solution below.(1) if your already have a
my_tuple
outside your function(not usingtemplate<typename ...T>
), You can use(2) if your havn't constructed
my_tuple
in your function and want to handle yourT ...arguments
尝试使用这个:
你可以这样使用:
Try to use this:
you can use this like this:
boost 的元组提供了辅助函数
get_head()
和get_tail()
,因此您的辅助函数可能如下所示:如此处所述 http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface .html
和
std::tuple
应该是类似的。实际上,不幸的是
std::tuple
似乎没有提供这样的接口,所以之前建议的方法应该可以工作,否则你需要切换到boost::tuple
它有其他好处(如已经提供的 io 运算符)。 虽然 gcc 的 boost::tuple 存在缺点 - 它还不接受可变参数模板,但这可能已经修复,因为我的机器上没有安装最新版本的 boost。boost's tuple provides helper functions
get_head()
andget_tail()
so your helper functions may look like this:as described in here http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html
with
std::tuple
it should be similar.Actually, unfortunately
std::tuple
does not seem to provide such interface, so methods suggested before should work, or you would need to switch toboost::tuple
which has other benefits (like io operators already provided). Though there is downside ofboost::tuple
with gcc - it does not accept variadic templates yet, but that may be already fixed as I do not have latest version of boost installed on my machine.我有一个基于 迭代元组的答案:
通常的想法是使用编译时递归。 事实上,这个想法用于制作一个类型安全的 printf,如原始元组论文中所述。
这可以很容易地推广到元组的
for_each
中:尽管这需要一些努力才能让
FuncT
为元组可能包含的每种类型表示具有适当重载的内容。 如果您知道所有元组元素将共享一个公共基类或类似的东西,那么这种方法效果最好。I have an answer based on Iterating over a Tuple:
The usual idea is to use compile time recursion. In fact, this idea is used to make a printf that is type safe as noted in the original tuple papers.
This can be easily generalized into a
for_each
for tuples:Though this then requires some effort to have
FuncT
represent something with the appropriate overloads for every type the tuple might contain. This works best if you know all the tuple elements will share a common base class or something similar.在 C++17 中,您可以使用
std::apply
< /a> 和 折叠表达式:打印元组的完整示例:
[Coliru 在线示例]
此解决方案解决了 M。 阿拉根的回答。
In C++17, you can use
std::apply
with fold expression:A complete example for printing a tuple:
[Online Example on Coliru]
This solution solves the issue of evaluation order in M. Alaggan's answer.