以自然(非反向)顺序将函数应用于 std::tuple 中的元素
Applying func to elements in std::tuple in the natural (not reverse) order
我需要为任意元组中的每个元素调用模板或重载函数。准确地说,我需要在元组中指定的元素上调用此函数。
例如。我有一个元组 std::tuple<int, float> t{1, 2.0f};
和一个函数
class Lambda{
public:
template<class T>
void operator()(T arg){ std::cout << arg << "; "; }
};
我需要一些 struct/function Apply
,如果像 Apply<Lambda, int, float>()(Lambda(), t)
这样调用会产生:
1; 2.0f;
而不是 2.0f; 1;
。
请注意,如果将 "raw" 参数包传递给函数,我知道解决方案,并且我知道如何以相反的顺序为元组执行此操作。但是以下部分专业化 Apply
的尝试失败了:
template<class Func, size_t index, class ...Components>
class ForwardsApplicator{
public:
void operator()(Func func, const std::tuple<Components...>& t){
func(std::get<index>(t));
ForwardsApplicator<Func, index + 1, Components...>()(func, t);
}
};
template<class Func, class... Components>
class ForwardsApplicator < Func, sizeof...(Components), Components... > {
public:
void operator()(Func func, const std::tuple<Components...>& t){}
};
int main{
ForwardsApplicator<Lambda, 0, int, float>()(Lambda{}, std::make_tuple(1, 2.0f));
}
代码已编译,但仅打印第一个参数。但是,如果我将 ForwardsApplicator
专业化替换为
template<class Func, class... Components>
class ForwardsApplicator < Func, 2, Components... >{...}
它工作正常——但是,当然,只适用于长度为 2 的元组。对于任意长度的元组,我该怎么做——如果可能的话,优雅地做到这一点?
编辑:谢谢大家的回答!这三者都非常直截了当,并从所有可能的有利角度解释了问题。
在模板中倒计时并不一定意味着您以相同的顺序处理元组元素。从头到尾处理元组的一种简单方法是头递归(与尾递归相反):
#include <tuple>
#include <iostream>
// Our entry point is the tuple size
template<typename Tuple, std::size_t i = std::tuple_size<Tuple>::value>
struct tuple_applicator {
template<typename Func> static void apply(Tuple &t, Func &&f) {
// but we recurse before we process the element associated with that
// number, reversing the order so that the front elements are processed
// first.
tuple_applicator<Tuple, i - 1>::apply(t, std::forward<Func>(f));
std::forward<Func>(f)(std::get<i - 1>(t));
}
};
// The recursion stops here.
template<typename Tuple>
struct tuple_applicator<Tuple, 0> {
template<typename Func> static void apply(Tuple &, Func &&) { }
};
// and this is syntactical sugar
template<typename Tuple, typename Func>
void tuple_apply(Tuple &t, Func &&f) {
tuple_applicator<Tuple>::apply(t, std::forward<Func>(f));
}
int main() {
std::tuple<int, double> t { 1, 2.3 };
// The generic lambda requires C++14, the rest
// works with C++11 as well. Put your Lambda here instead.
tuple_apply(t, [](auto x) { std::cout << x * 2 << '\n'; });
}
输出为
2
4.6
问题是 size...(Components)
不能用于未知类型列表 Components
的特化。 GCC 抱怨这个错误:
prog.cpp:16:7: error: template argument 'sizeof... (Components)' involves template parameter(s)
class ForwardsApplicator < Func, sizeof...(Components), Components... > {
^
我建议采用稍微不同的方法。首先,将类型列表 Components
移动到 operator()
的模板参数中,即:
template<class ...Components>
void operator()(Func func, const std::tuple<Components...>& t) {
...
}
然后,反转调用顺序:首先进行递归调用,然后使用 index-1
调用(即调用 last 元组元素)。从 index = sizeof...(Components)
开始这个递归,一直到 index = 0
,这是 noop(所以特化有 0
,独立于 sizeof...(Components)
,这是我开始谈论的问题)。
为了帮助调用它,添加一个用于模板参数推导的函数:
// General case (recursion)
template<class Func, size_t index>
class ForwardsApplicator{
public:
template<class ...Components>
void operator()(Func func, const std::tuple<Components...>& t){
ForwardsApplicator<Func, index - 1>()(func, t);
func(std::get<index - 1>(t));
}
};
// Special case (stop recursion)
template<class Func>
class ForwardsApplicator<Func, 0> {
public:
template<class ...Components>
void operator()(Func func, const std::tuple<Components...>& t){}
};
// Helper function for template type deduction
template<class Func, class ...Components>
void apply(Func func, const std::tuple<Components...>& t) {
ForwardsApplicator<Func, sizeof...(Components)>()(func, t);
}
然后很容易调用,调用站点上不需要任何模板参数:
apply(Lambda{}, std::make_tuple(1, 2.0f));
这是 integer_sequence
技巧的教科书案例。
template<class Func, class Tuple, size_t...Is>
void for_each_in_tuple(Func f, Tuple&& tuple, std::index_sequence<Is...>){
using expander = int[];
(void)expander { 0, ((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... };
}
template<class Func, class Tuple>
void for_each_in_tuple(Func f, Tuple&& tuple){
for_each_in_tuple(f, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
Demo.
std::index_sequence
和朋友是 C++14,但它是一个纯库扩展,可以很容易地在 C++11 中实现。您可以在 SO 上轻松找到六个实现。
我需要为任意元组中的每个元素调用模板或重载函数。准确地说,我需要在元组中指定的元素上调用此函数。
例如。我有一个元组 std::tuple<int, float> t{1, 2.0f};
和一个函数
class Lambda{
public:
template<class T>
void operator()(T arg){ std::cout << arg << "; "; }
};
我需要一些 struct/function Apply
,如果像 Apply<Lambda, int, float>()(Lambda(), t)
这样调用会产生:
1; 2.0f;
而不是 2.0f; 1;
。
请注意,如果将 "raw" 参数包传递给函数,我知道解决方案,并且我知道如何以相反的顺序为元组执行此操作。但是以下部分专业化 Apply
的尝试失败了:
template<class Func, size_t index, class ...Components>
class ForwardsApplicator{
public:
void operator()(Func func, const std::tuple<Components...>& t){
func(std::get<index>(t));
ForwardsApplicator<Func, index + 1, Components...>()(func, t);
}
};
template<class Func, class... Components>
class ForwardsApplicator < Func, sizeof...(Components), Components... > {
public:
void operator()(Func func, const std::tuple<Components...>& t){}
};
int main{
ForwardsApplicator<Lambda, 0, int, float>()(Lambda{}, std::make_tuple(1, 2.0f));
}
代码已编译,但仅打印第一个参数。但是,如果我将 ForwardsApplicator
专业化替换为
template<class Func, class... Components>
class ForwardsApplicator < Func, 2, Components... >{...}
它工作正常——但是,当然,只适用于长度为 2 的元组。对于任意长度的元组,我该怎么做——如果可能的话,优雅地做到这一点?
编辑:谢谢大家的回答!这三者都非常直截了当,并从所有可能的有利角度解释了问题。
在模板中倒计时并不一定意味着您以相同的顺序处理元组元素。从头到尾处理元组的一种简单方法是头递归(与尾递归相反):
#include <tuple>
#include <iostream>
// Our entry point is the tuple size
template<typename Tuple, std::size_t i = std::tuple_size<Tuple>::value>
struct tuple_applicator {
template<typename Func> static void apply(Tuple &t, Func &&f) {
// but we recurse before we process the element associated with that
// number, reversing the order so that the front elements are processed
// first.
tuple_applicator<Tuple, i - 1>::apply(t, std::forward<Func>(f));
std::forward<Func>(f)(std::get<i - 1>(t));
}
};
// The recursion stops here.
template<typename Tuple>
struct tuple_applicator<Tuple, 0> {
template<typename Func> static void apply(Tuple &, Func &&) { }
};
// and this is syntactical sugar
template<typename Tuple, typename Func>
void tuple_apply(Tuple &t, Func &&f) {
tuple_applicator<Tuple>::apply(t, std::forward<Func>(f));
}
int main() {
std::tuple<int, double> t { 1, 2.3 };
// The generic lambda requires C++14, the rest
// works with C++11 as well. Put your Lambda here instead.
tuple_apply(t, [](auto x) { std::cout << x * 2 << '\n'; });
}
输出为
2
4.6
问题是 size...(Components)
不能用于未知类型列表 Components
的特化。 GCC 抱怨这个错误:
prog.cpp:16:7: error: template argument 'sizeof... (Components)' involves template parameter(s) class ForwardsApplicator < Func, sizeof...(Components), Components... > { ^
我建议采用稍微不同的方法。首先,将类型列表 Components
移动到 operator()
的模板参数中,即:
template<class ...Components>
void operator()(Func func, const std::tuple<Components...>& t) {
...
}
然后,反转调用顺序:首先进行递归调用,然后使用 index-1
调用(即调用 last 元组元素)。从 index = sizeof...(Components)
开始这个递归,一直到 index = 0
,这是 noop(所以特化有 0
,独立于 sizeof...(Components)
,这是我开始谈论的问题)。
为了帮助调用它,添加一个用于模板参数推导的函数:
// General case (recursion)
template<class Func, size_t index>
class ForwardsApplicator{
public:
template<class ...Components>
void operator()(Func func, const std::tuple<Components...>& t){
ForwardsApplicator<Func, index - 1>()(func, t);
func(std::get<index - 1>(t));
}
};
// Special case (stop recursion)
template<class Func>
class ForwardsApplicator<Func, 0> {
public:
template<class ...Components>
void operator()(Func func, const std::tuple<Components...>& t){}
};
// Helper function for template type deduction
template<class Func, class ...Components>
void apply(Func func, const std::tuple<Components...>& t) {
ForwardsApplicator<Func, sizeof...(Components)>()(func, t);
}
然后很容易调用,调用站点上不需要任何模板参数:
apply(Lambda{}, std::make_tuple(1, 2.0f));
这是 integer_sequence
技巧的教科书案例。
template<class Func, class Tuple, size_t...Is>
void for_each_in_tuple(Func f, Tuple&& tuple, std::index_sequence<Is...>){
using expander = int[];
(void)expander { 0, ((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... };
}
template<class Func, class Tuple>
void for_each_in_tuple(Func f, Tuple&& tuple){
for_each_in_tuple(f, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
Demo.
std::index_sequence
和朋友是 C++14,但它是一个纯库扩展,可以很容易地在 C++11 中实现。您可以在 SO 上轻松找到六个实现。