无需递归调用一个对象上每个元组元素的函数
Call function for each tuple element on one object without recursion
我有一个 class A
的对象,它可以用不同的类型调用,并且 returns 在每次调用时都改变了自己。对于这个问题的目的 A
会做
struct A {
A call(const int&) {
}
A call(const string& s) {
}
////
} a;
所以我有一个未知类型的元组:
std::tuple<Types...> t;
我想用每个元组元素调用 a
,所以我想得到类似的东西:
b = a;
b = b.call(get<0>(t));
b = b.call(get<1>(t));
b = b.call(get<2>(t));
//...
或
b = a.call(get<0>(t)).call(get<1>(t)).call(get<2>(t)...)
顺序并不是很重要(我的意思是如果调用顺序颠倒甚至打乱也没关系)。
我知道递归是可行的,但它很难看。不递归是否可以实现?
您可以使用 boost.fusion 迭代元组
在“get part of std::tuple”的帮助下:
#include <iostream>
#include <string>
#include <algorithm>
#include <tuple>
//SEE
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
template <size_t... n>
struct ct_integers_list {
template <size_t m>
struct push_back
{
typedef ct_integers_list<n..., m> type;
};
};
template <size_t max>
struct ct_iota_1
{
typedef typename ct_iota_1<max - 1>::type::template push_back<max>::type type;
};
template <>
struct ct_iota_1<0>
{
typedef ct_integers_list<> type;
};
template <size_t... indices, typename Tuple>
auto tuple_subset(const Tuple& tpl, ct_integers_list<indices...>)
-> decltype(std::make_tuple(std::get<indices>(tpl)...))
{
return std::make_tuple(std::get<indices>(tpl)...);
// this means:
// make_tuple(get<indices[0]>(tpl), get<indices[1]>(tpl), ...)
}
template <typename Head, typename... Tail>
std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& tpl)
{
return tuple_subset(tpl, typename ct_iota_1<sizeof...(Tail)>::type());
// this means:
// tuple_subset<1, 2, 3, ..., sizeof...(Tail)-1>(tpl, ..)
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//SEE
struct A
{
A call(const int&) {
std::cout << "int" << std::endl;
return *this;
}
A call(const std::string& s) {
std::cout << "string" << std::endl;
return *this;
}
template <typename T>
A call(std::tuple<T> tpl)
{
return call(std::get<0>(tpl));
}
template <typename T, typename...Types>
A call(std::tuple<T, Types...> tpl)
{
return call(std::get<0>(tpl)).call(tuple_tail(tpl));
}
} a;
int main()
{
std::tuple<int, std::string, int, int, std::string> t(0, "1", 2, 3, "4");
A b = a.call(t);
}
输出:
int
string
int
int
string
您可以使用 std::index_sequence<Is...>
,例如:
namespace detail
{
template <std::size_t...Is, typename T>
void a_call(A& a, std::index_sequence<Is...>, const T& t)
{
int dummy[] = {0, ((a = a.call(std::get<Is>(t))), void(), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable.
}
}
template <typename ... Ts>
void a_call(A& a, const std::tuple<Ts...>& t)
{
detail::a_call(a, std::index_sequence_for<Ts...>{}, t);
}
在 C++17 中,折叠表达式允许:
template <std::size_t...Is, typename T>
void a_call(A& a, std::index_sequence<Is...>, const T& t)
{
(static_cast<void>(a = a.call(std::get<Is>(t))), ...);
}
甚至 std::apply
:
template <typename ... Ts>
void a_call(A& a, const std::tuple<Ts...>& t)
{
std::apply([&](const auto&... args){ (static_cast<void>(a = a.call(args)), ...); }, t);
}
使用std::integer_sequence和std::make_integer_sequence如下图:
struct A {
A call(const int&) {
std::cout << "Calling A::call(int)" << std::endl;
return A{};
}
A call(const std::string& s) {
std::cout << "Calling A::call(std::string)" << std::endl;
return A{};
}
////
} a;
template<typename Sequence>
struct call_helper;
template<std::size_t Current, std::size_t... Rest>
struct call_helper<std::integer_sequence<std::size_t, Current, Rest...>>
{
template<typename Tuple, typename Result>
static auto call(Tuple const& tup, Result&& res)
{
// Call the next helper with the rest sequence
return call_helper<
std::integer_sequence<std::size_t, Rest...>
>::call(tup, res.call(std::get<Current>(tup)));
}
};
template<>
struct call_helper<std::integer_sequence<std::size_t>>
{
// End reached, just return the value
template<typename Tuple, typename Result>
static auto call(Tuple const&, Result&& res)
-> std::decay_t<Result>
{
return std::forward<Result>(res);
}
};
template<typename Tuple, typename Result>
auto call_all(Tuple const& tup, Result res)
{
return call_helper<std::make_integer_sequence<
std::size_t, std::tuple_size<Tuple>::value>
>::call(tup, std::forward<Result>(res));
}
// Test call:
std::tuple<int, std::string, int, std::string, std::string> mytup;
call_all(mytup, a);
我有一个 class A
的对象,它可以用不同的类型调用,并且 returns 在每次调用时都改变了自己。对于这个问题的目的 A
会做
struct A {
A call(const int&) {
}
A call(const string& s) {
}
////
} a;
所以我有一个未知类型的元组:
std::tuple<Types...> t;
我想用每个元组元素调用 a
,所以我想得到类似的东西:
b = a;
b = b.call(get<0>(t));
b = b.call(get<1>(t));
b = b.call(get<2>(t));
//...
或
b = a.call(get<0>(t)).call(get<1>(t)).call(get<2>(t)...)
顺序并不是很重要(我的意思是如果调用顺序颠倒甚至打乱也没关系)。
我知道递归是可行的,但它很难看。不递归是否可以实现?
您可以使用 boost.fusion 迭代元组
在“get part of std::tuple”的帮助下:
#include <iostream>
#include <string>
#include <algorithm>
#include <tuple>
//SEE
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
template <size_t... n>
struct ct_integers_list {
template <size_t m>
struct push_back
{
typedef ct_integers_list<n..., m> type;
};
};
template <size_t max>
struct ct_iota_1
{
typedef typename ct_iota_1<max - 1>::type::template push_back<max>::type type;
};
template <>
struct ct_iota_1<0>
{
typedef ct_integers_list<> type;
};
template <size_t... indices, typename Tuple>
auto tuple_subset(const Tuple& tpl, ct_integers_list<indices...>)
-> decltype(std::make_tuple(std::get<indices>(tpl)...))
{
return std::make_tuple(std::get<indices>(tpl)...);
// this means:
// make_tuple(get<indices[0]>(tpl), get<indices[1]>(tpl), ...)
}
template <typename Head, typename... Tail>
std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& tpl)
{
return tuple_subset(tpl, typename ct_iota_1<sizeof...(Tail)>::type());
// this means:
// tuple_subset<1, 2, 3, ..., sizeof...(Tail)-1>(tpl, ..)
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//SEE
struct A
{
A call(const int&) {
std::cout << "int" << std::endl;
return *this;
}
A call(const std::string& s) {
std::cout << "string" << std::endl;
return *this;
}
template <typename T>
A call(std::tuple<T> tpl)
{
return call(std::get<0>(tpl));
}
template <typename T, typename...Types>
A call(std::tuple<T, Types...> tpl)
{
return call(std::get<0>(tpl)).call(tuple_tail(tpl));
}
} a;
int main()
{
std::tuple<int, std::string, int, int, std::string> t(0, "1", 2, 3, "4");
A b = a.call(t);
}
输出:
int
string
int
int
string
您可以使用 std::index_sequence<Is...>
,例如:
namespace detail
{
template <std::size_t...Is, typename T>
void a_call(A& a, std::index_sequence<Is...>, const T& t)
{
int dummy[] = {0, ((a = a.call(std::get<Is>(t))), void(), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable.
}
}
template <typename ... Ts>
void a_call(A& a, const std::tuple<Ts...>& t)
{
detail::a_call(a, std::index_sequence_for<Ts...>{}, t);
}
在 C++17 中,折叠表达式允许:
template <std::size_t...Is, typename T>
void a_call(A& a, std::index_sequence<Is...>, const T& t)
{
(static_cast<void>(a = a.call(std::get<Is>(t))), ...);
}
甚至 std::apply
:
template <typename ... Ts>
void a_call(A& a, const std::tuple<Ts...>& t)
{
std::apply([&](const auto&... args){ (static_cast<void>(a = a.call(args)), ...); }, t);
}
使用std::integer_sequence和std::make_integer_sequence如下图:
struct A {
A call(const int&) {
std::cout << "Calling A::call(int)" << std::endl;
return A{};
}
A call(const std::string& s) {
std::cout << "Calling A::call(std::string)" << std::endl;
return A{};
}
////
} a;
template<typename Sequence>
struct call_helper;
template<std::size_t Current, std::size_t... Rest>
struct call_helper<std::integer_sequence<std::size_t, Current, Rest...>>
{
template<typename Tuple, typename Result>
static auto call(Tuple const& tup, Result&& res)
{
// Call the next helper with the rest sequence
return call_helper<
std::integer_sequence<std::size_t, Rest...>
>::call(tup, res.call(std::get<Current>(tup)));
}
};
template<>
struct call_helper<std::integer_sequence<std::size_t>>
{
// End reached, just return the value
template<typename Tuple, typename Result>
static auto call(Tuple const&, Result&& res)
-> std::decay_t<Result>
{
return std::forward<Result>(res);
}
};
template<typename Tuple, typename Result>
auto call_all(Tuple const& tup, Result res)
{
return call_helper<std::make_integer_sequence<
std::size_t, std::tuple_size<Tuple>::value>
>::call(tup, std::forward<Result>(res));
}
// Test call:
std::tuple<int, std::string, int, std::string, std::string> mytup;
call_all(mytup, a);