我做了以下元组:
我想知道如何迭代它?有tupl_size(),但阅读文档,我没有得到如何利用它.我也搜索了SO,但问题似乎就在附近Boost::tuple.
auto some = make_tuple("I am good", 255, 2.1);
Run Code Online (Sandbox Code Playgroud)
T.C*_*.C. 28
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)>());
}
Run Code Online (Sandbox Code Playgroud)
用法:
auto some = std::make_tuple("I am good", 255, 2.1);
for_each_in_tuple(some, [](const auto &x) { std::cout << x << std::endl; });
Run Code Online (Sandbox Code Playgroud)
演示.
std::index_sequence和C系列是C++ 14的特性,但它们可以很容易地用C++ 11实现(SO上有很多可用的).多态lambda也是C++ 14,但可以用自定义编写的仿函数替换.
Yak*_*ont 25
这是尝试将元组的迭代分解为组件部分.
首先,表示按顺序执行一系列操作的函数.请注意,许多编译器发现这很难理解,尽管它是合法的C++ 11,据我所知:
template<class... Fs>
void do_in_order( Fs&&... fs ) {
int unused[] = { 0, ( (void)std::forward<Fs>(fs)(), 0 )... }
(void)unused; // blocks warnings
}
Run Code Online (Sandbox Code Playgroud)
接下来,一个函数采用a std::tuple,并提取访问每个元素所需的索引.通过这样做,我们可以在以后完善前进.
作为附带好处,我的代码支持std::pair和std::array迭代:
template<class T>
constexpr std::make_index_sequence<std::tuple_size<T>::value>
get_indexes( T const& )
{ return {}; }
Run Code Online (Sandbox Code Playgroud)
肉和土豆:
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)) ); }... );
}
Run Code Online (Sandbox Code Playgroud)
和面向公众的界面:
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) );
}
Run Code Online (Sandbox Code Playgroud)
虽然它声明Tuple它适用于std::arrays和std::pairs.它还将所述对象的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
}
Run Code Online (Sandbox Code Playgroud)
这不会花费太多冗长,但我个人觉得不太清楚.do_in_order在我看来,通过内联实现,工作方式的影子魔法是模糊不清的.
index_sequence(和支持模板)是一个C++ 14功能,可以用C++ 11编写.在堆栈溢出上找到这样的实现很容易.目前最热门的谷歌热门是一个体面的O(lg(n))深度实现,如果我正确阅读评论可能是实际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)...};
}
Run Code Online (Sandbox Code Playgroud)
然后:
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))...
);
}
Run Code Online (Sandbox Code Playgroud)
这避免了手动扩展,但编译更多的编译器.我们传递Isvia auto&&i参数.
在C++ 1Z我们还可以使用std::apply与for_each_arg函数对象废除指数摆弄.
dav*_*igh 11
这是一个类似的,比TC先前接受的更详细的解决方案,它可能更容易理解( - 它可能与网络中的其他数千个相同):
template<typename TupleType, typename FunctionType>
void for_each(TupleType&&, FunctionType
, std::integral_constant<size_t, std::tuple_size<typename std::remove_reference<TupleType>::type >::value>) {}
template<std::size_t I, typename TupleType, typename FunctionType
, typename = typename std::enable_if<I!=std::tuple_size<typename std::remove_reference<TupleType>::type>::value>::type >
void for_each(TupleType&& t, FunctionType f, std::integral_constant<size_t, I>)
{
f(std::get<I>(std::forward<TupleType>(t)));
for_each(std::forward<TupleType>(t), f, std::integral_constant<size_t, I + 1>());
}
template<typename TupleType, typename FunctionType>
void for_each(TupleType&& t, FunctionType f)
{
for_each(std::forward<TupleType>(t), f, std::integral_constant<size_t, 0>());
}
Run Code Online (Sandbox Code Playgroud)
用法(带std::tuple):
auto some = std::make_tuple("I am good", 255, 2.1);
for_each(some, [](const auto &x) { std::cout << x << std::endl; });
Run Code Online (Sandbox Code Playgroud)
用法(带std::array):
std::array<std::string,2> some2 = {"Also good", "Hello world"};
for_each(some2, [](const auto &x) { std::cout << x << std::endl; });
Run Code Online (Sandbox Code Playgroud)
总体思路:如在TC的解决方案中,从索引开始I=0并达到元组的大小.然而,这里不是按照可变的扩展而是一次一个地进行.
说明:
for_each如果I等于元组的大小,则调用第一个重载.然后函数什么都不做,这样就结束了递归.
第二个重载使用参数调用函数,std::get<I>(t)并将索引增加1.std::integral_constant需要该类才能解析I编译时的值.该std::enable_ifSFINAE东西是用来帮助编译器从以前的一个分离这种过载,并调用此重载仅在I比元组大小(上Coliru这是必要的,而在Visual Studio它工作没有).
第三个开始递归I=0.通常从外部调用过载.
编辑:我还包括Yakk提到的另外支持的想法,std::array并std::pair使用一般模板参数TupleType而不是专门用于std::tuple<Ts ...>.
由于TupleType类型需要推断并且是这样的"通用参考",这进一步具有免费获得完美转发的优点.缺点是必须使用另一个间接通道typename std::remove_reference<TupleType>::type,因为TupleType也可能推断为参考类型.
| 归档时间: |
|
| 查看次数: |
23810 次 |
| 最近记录: |