我该如何使用C++11迭代元组?我尝试了以下代码:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
但是这样不起作用:
错误 1:抱歉,未实现:无法将“Listener …”扩展为固定长度的参数列表。
错误 2:i 不能出现在常量表达式中。
那么,我应该如何正确地迭代元组中的元素?
我该如何使用C++11迭代元组?我尝试了以下代码:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
但是这样不起作用:
错误 1:抱歉,未实现:无法将“Listener …”扩展为固定长度的参数列表。
错误 2:i 不能出现在常量表达式中。
那么,我应该如何正确地迭代元组中的元素?
我基于遍历元组的答案:
#include <tuple>
#include <utility>
#include <iostream>
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
print(std::tuple<Tp...>& t)
{ }
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
print(std::tuple<Tp...>& t)
{
std::cout << std::get<I>(t) << std::endl;
print<I + 1, Tp...>(t);
}
int
main()
{
typedef std::tuple<int, float, double> T;
T t = std::make_tuple(2, 3.14159F, 2345.678);
print(t);
}
for_each
中:#include <tuple>
#include <utility>
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
{ }
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT f)
{
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
虽然这需要一些努力来让 FuncT
表示出每种类型的元组所需的适当重载。如果您知道所有元组元素将共享一个公共基类或类似的东西,则效果最佳。
const std::tuple<Tp...>&
版本的代码。如果您在迭代时不打算修改元组,则这些 const
版本就足够了。 - lethal-guitarstruct printer { template<typename T> void operator()(const T& t) const { ... } };
,因为传递一个“模板打印机”是行不通的。 - Algebraic Pavelstd::apply
与 fold 表达式:std::apply([](auto&&... args) {((/* args.dosomething() */), ...);}, the_tuple);
打印元组的完整示例:
#include <tuple>
#include <iostream>
int main()
{
std::tuple t{42, 'a', 4.2}; // Another C++17 feature: class template argument deduction
std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}
此解决方案解决了M. Alaggan的答案中的评估顺序问题。
((std::cout << args << '\n'), ...);
?lambda 表达式会将元组元素作为 args
展开并调用一次,但是双括号是怎么回事? - helmesjo((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))
。 - xskxzrauto&&
可以捕获rvalue和lvalue,但是auto&
只能捕获lvalue?那么除非我要限制为只使用lvalue,否则auto&&
总是更好的选择? - starriet#define FOR_EACH(tuple, callback) \ apply([](auto&&... args) { \ ((callback(args)), ...); \ }, tuple);
- undefined在C++17中,你可以这样做:
std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple);
在Clang++ 3.9中,使用std::experimental::apply已经可以运行。
do_something()
的调用 - 以未指定的顺序发生,因为参数包在函数调用 ()
中展开,其中参数的顺序是未指定的?这可能非常重要;我想大多数人都希望按照成员索引的顺序(即作为 std::get<>()
的参数)保证顺序相同。据我所知,在这种情况下获得保证顺序,展开必须在 {花括号}
中完成。我错了吗?此答案强调了此类顺序: https://dev59.com/4WQo5IYBdhLWcg3wBLfy#16387374 - underscore_dfor(auto [i, j], ...
。 - gonidelisBoost.Fusion 是一种可能性:
未经测试的示例:
struct DoSomething
{
template<typename T>
void operator()(T& t) const
{
t.do_sth();
}
};
tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());
使用C++17中的if constexpr
可以更简单、直观和编译器友好地完成这个任务:
// prints every element of a tuple
template<size_t I = 0, typename... Tp>
void print(std::tuple<Tp...>& t) {
std::cout << std::get<I>(t) << " ";
// do things
if constexpr(I+1 != sizeof...(Tp))
print<I+1>(t);
}
使用 Boost.Hana 和泛型 lambda:
#include <tuple>
#include <iostream>
#include <boost/hana.hpp>
#include <boost/hana/ext/std/tuple.hpp>
struct Foo1 {
int foo() const { return 42; }
};
struct Foo2 {
int bar = 0;
int foo() { bar = 24; return bar; }
};
int main() {
using namespace std;
using boost::hana::for_each;
Foo1 foo1;
Foo2 foo2;
for_each(tie(foo1, foo2), [](auto &foo) {
cout << foo.foo() << endl;
});
cout << "foo2.bar after mutation: " << foo2.bar << endl;
}
boost::fusion
(特别是与using namespace std
一起使用)。现在无法知道for_each
是std::for_each
还是boost::fusion::for_each
。 - Bulletmagnet这是一个简单的C++17方法,只使用标准库就可以迭代元组项:
#include <tuple> // std::tuple
#include <functional> // std::invoke
template <
size_t Index = 0, // start iteration at 0 index
typename TTuple, // the tuple type
size_t Size =
std::tuple_size_v<
std::remove_reference_t<TTuple>>, // tuple size
typename TCallable, // the callable to be invoked for each tuple item
typename... TArgs // other arguments to be passed to the callable
>
void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
if constexpr (Index < Size)
{
std::invoke(callable, args..., std::get<Index>(tuple));
if constexpr (Index + 1 < Size)
for_each<Index + 1>(
std::forward<TTuple>(tuple),
std::forward<TCallable>(callable),
std::forward<TArgs>(args)...);
}
}
例子:
#include <iostream>
int main()
{
std::tuple<int, char> items{1, 'a'};
for_each(items, [](const auto& item) {
std::cout << item << "\n";
});
}
输出:
1
a
如果可调用对象返回一个值(但仍然可以使用不返回可分配布尔值的可调用对象,例如void),则可以将其扩展为有条件地中断循环:
#include <tuple> // std::tuple
#include <functional> // std::invoke
template <
size_t Index = 0, // start iteration at 0 index
typename TTuple, // the tuple type
size_t Size =
std::tuple_size_v<
std::remove_reference_t<TTuple>>, // tuple size
typename TCallable, // the callable to bo invoked for each tuple item
typename... TArgs // other arguments to be passed to the callable
>
void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
if constexpr (Index < Size)
{
if constexpr (std::is_assignable_v<bool&, std::invoke_result_t<TCallable&&, TArgs&&..., decltype(std::get<Index>(tuple))>>)
{
if (!std::invoke(callable, args..., std::get<Index>(tuple)))
return;
}
else
{
std::invoke(callable, args..., std::get<Index>(tuple));
}
if constexpr (Index + 1 < Size)
for_each<Index + 1>(
std::forward<TTuple>(tuple),
std::forward<TCallable>(callable),
std::forward<TArgs>(args)...);
}
}
例子:
#include <iostream>
int main()
{
std::tuple<int, char> items{ 1, 'a' };
for_each(items, [](const auto& item) {
std::cout << item << "\n";
});
std::cout << "---\n";
for_each(items, [](const auto& item) {
std::cout << item << "\n";
return false;
});
}
输出:
1
a
---
1
您需要使用模板元编程,这里展示了 Boost.Tuple 的用法:
#include <boost/tuple/tuple.hpp>
#include <iostream>
template <typename T_Tuple, size_t size>
struct print_tuple_helper {
static std::ostream & print( std::ostream & s, const T_Tuple & t ) {
return print_tuple_helper<T_Tuple,size-1>::print( s, t ) << boost::get<size-1>( t );
}
};
template <typename T_Tuple>
struct print_tuple_helper<T_Tuple,0> {
static std::ostream & print( std::ostream & s, const T_Tuple & ) {
return s;
}
};
template <typename T_Tuple>
std::ostream & print_tuple( std::ostream & s, const T_Tuple & t ) {
return print_tuple_helper<T_Tuple,boost::tuples::length<T_Tuple>::value>::print( s, t );
}
int main() {
const boost::tuple<int,char,float,char,double> t( 0, ' ', 2.5f, '\n', 3.1416 );
print_tuple( std::cout, t );
return 0;
}
print_tuple()
编写为变长模板函数。首先定义一些索引帮助器:
template <size_t ...I>
struct index_sequence {};
template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};
template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};
您想在每个元组元素上应用您的函数:
template <typename T>
/* ... */ foo(T t) { /* ... */ }
您可以编写:
template<typename ...T, size_t ...I>
/* ... */ do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
std::tie(foo(std::get<I>(ts)) ...);
}
template <typename ...T>
/* ... */ do_foo(std::tuple<T...> &ts) {
return do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}
如果foo
返回void
,请使用:
std::tie((foo(std::get<I>(ts)), 1) ... );
make_index_sequence
已经定义(http://en.cppreference.com/w/cpp/utility/integer_sequence)。template <typename T, typename ...R>
void do_foo_iter(T t, R ...r) {
foo(t);
do_foo(r...);
}
void do_foo_iter() {}
template<typename ...T, size_t ...I>
void do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
do_foo_iter(std::get<I>(ts) ...);
}
template <typename ...T>
void do_foo(std::tuple<T...> &ts) {
do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}
operator,
之前,应该将 foo
的返回值强制转换为 void
,以避免可能出现的病态运算符重载。 - Yakk - Adam Nevraumont