std::transform 等价于元组
Equivalent of std::transform for tuples
我想要一个对元组表现得像 std::transform
的函数。基本上要实现的功能是
template<size_t From, size_t To, class Tuple, class Func>
void tuple_transform(Tuple&& source, Tuple&& target, Func f)
{
// elements <From, To> of `target` ti become `f(si)`, where
// si is the corresponding element of `source`
};
我相信要实现这个,我需要一个 编译时整数范围 结构,std::index_sequence
的概括,我已经实现了它 here with cti::range
. I also believe that 类型的编译时遍历在这里是理想的:
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>());
}
有人可以帮我实现吗?
注意事项:关于变异函数的类型
@MohitJain 就像代码中链接的那样 (tuple_transform) 没有考虑到这一点(Func
具有单一类型)。如果这样解决,我可以通过传递模板模板参数 template<class> class Func
并强制我的转换类型像这样
来轻松扩展它
template<typename T>
struct Func
{
static void apply(T& val) { ... }
}
然后在元组转换的内部,每个转换函数都可以这样调用:
get<I>(target) = func<typename tuple_element<I, Tuple>::type>::apply(get<I>(source))
编辑
刚刚在accu 2015 做了一个闪电演讲。以上是演讲结束时的CodeKata。我将离开 here 演示文稿,希望它能对任何实施尝试有所帮助(我认为几乎所有需要的工具都已介绍,因此我们将对此进行更多尝试)
这是一个使用 here 中的 index_range
的解决方案。
template<size_t SN, size_t DN, class TSrc, class TDest, class Func>
void tuple_call_assign(TSrc&& source, TDest& target, Func f)
{
std::get<DN>(target) = f(std::get<SN>(std::forward<TSrc>(source)));
}
template<size_t From, size_t To, class TSrc, class TDest, class Func, size_t...Is, size_t...DIs>
void tuple_transform(TSrc&& source, TDest& target, Func f,
std::index_sequence<Is...>, std::index_sequence<DIs...>)
{
using expander = int[];
(void)expander { 0, (tuple_call_assign<Is,DIs>(std::forward<TSrc>(source),target,f), 0)... };
}
template<size_t From, size_t To, size_t FromDest, class TSrc, class TDest, class Func>
void tuple_transform(TSrc&& source, TDest& target, Func f)
{
static_assert(To > From, "Range must be increasing");
static_assert(To <= std::tuple_size<std::decay_t<TSrc>>::value+1,
"Range must be valid for source tuple");
constexpr size_t RangeSize = To-From;
static_assert(FromDest+RangeSize <= std::tuple_size<std::decay_t<TDest>>::value,
"Range must be valid for target tuple");
tuple_transform<From,To>(std::forward<TSrc>(source), target, f,
index_range<From,To>(), index_range<FromDest, FromDest+RangeSize>());
}
这需要第三个模板参数来指定要转换为目标元组的起始索引。
index_sequence
解决方案已经提到:
template <std::size_t From, size_t... indices, typename T1, typename T2, typename Func>
void transform(T1&& s, T2& t, Func f, std::index_sequence<indices...>)
{
(void)std::initializer_list<int>{
(std::get<indices+From>(t) = f(std::get<indices>(std::forward<T1>(s))), 0)...};
}
template <std::size_t From, std::size_t To, typename T1, typename T2, typename Func>
void transform(T1&& s, T2& t, Func f)
{
transform<From>(std::forward<T1>(s), t, f, std::make_index_sequence<To-From+1>());
}
Demo.
我想要一个对元组表现得像 std::transform
的函数。基本上要实现的功能是
template<size_t From, size_t To, class Tuple, class Func>
void tuple_transform(Tuple&& source, Tuple&& target, Func f)
{
// elements <From, To> of `target` ti become `f(si)`, where
// si is the corresponding element of `source`
};
我相信要实现这个,我需要一个 编译时整数范围 结构,std::index_sequence
的概括,我已经实现了它 here with cti::range
. I also believe that
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>());
}
有人可以帮我实现吗?
注意事项:关于变异函数的类型
@MohitJain 就像代码中链接的那样 (tuple_transform) 没有考虑到这一点(Func
具有单一类型)。如果这样解决,我可以通过传递模板模板参数 template<class> class Func
并强制我的转换类型像这样
template<typename T>
struct Func
{
static void apply(T& val) { ... }
}
然后在元组转换的内部,每个转换函数都可以这样调用:
get<I>(target) = func<typename tuple_element<I, Tuple>::type>::apply(get<I>(source))
编辑
刚刚在accu 2015 做了一个闪电演讲。以上是演讲结束时的CodeKata。我将离开 here 演示文稿,希望它能对任何实施尝试有所帮助(我认为几乎所有需要的工具都已介绍,因此我们将对此进行更多尝试)
这是一个使用 here 中的 index_range
的解决方案。
template<size_t SN, size_t DN, class TSrc, class TDest, class Func>
void tuple_call_assign(TSrc&& source, TDest& target, Func f)
{
std::get<DN>(target) = f(std::get<SN>(std::forward<TSrc>(source)));
}
template<size_t From, size_t To, class TSrc, class TDest, class Func, size_t...Is, size_t...DIs>
void tuple_transform(TSrc&& source, TDest& target, Func f,
std::index_sequence<Is...>, std::index_sequence<DIs...>)
{
using expander = int[];
(void)expander { 0, (tuple_call_assign<Is,DIs>(std::forward<TSrc>(source),target,f), 0)... };
}
template<size_t From, size_t To, size_t FromDest, class TSrc, class TDest, class Func>
void tuple_transform(TSrc&& source, TDest& target, Func f)
{
static_assert(To > From, "Range must be increasing");
static_assert(To <= std::tuple_size<std::decay_t<TSrc>>::value+1,
"Range must be valid for source tuple");
constexpr size_t RangeSize = To-From;
static_assert(FromDest+RangeSize <= std::tuple_size<std::decay_t<TDest>>::value,
"Range must be valid for target tuple");
tuple_transform<From,To>(std::forward<TSrc>(source), target, f,
index_range<From,To>(), index_range<FromDest, FromDest+RangeSize>());
}
这需要第三个模板参数来指定要转换为目标元组的起始索引。
index_sequence
解决方案已经提到:
template <std::size_t From, size_t... indices, typename T1, typename T2, typename Func>
void transform(T1&& s, T2& t, Func f, std::index_sequence<indices...>)
{
(void)std::initializer_list<int>{
(std::get<indices+From>(t) = f(std::get<indices>(std::forward<T1>(s))), 0)...};
}
template <std::size_t From, std::size_t To, typename T1, typename T2, typename Func>
void transform(T1&& s, T2& t, Func f)
{
transform<From>(std::forward<T1>(s), t, f, std::make_index_sequence<To-From+1>());
}
Demo.