问题: 使元组元组“简单”的最佳方法是什么?例如。一维。
案例1
template <class Field>
auto process_field(Field&& field){
// or another std::get<ids>(field)...
return std::forward_as_tuple(field.fst, field.snd, field.thrd, field.fth);
}
template <std::size_t ...ids, class Rec>
auto iterate_record(std::index_sequence<ids...>, Rec && rec){
return forward_as_tuple(
process_field
(std::get<ids>( forward<Rec>(rec).data))...
);
}
iterate_record
返回元组的元组。我需要简单的元组。
也许我需要iterate_record
周围的某种包装,或者直接围绕这个包装:process_field (std::get<ids>( forward<Rec>(rec).data))...
情况2
我有一个元组元组(具体类型,而不是引用)。如何有效地将其复制到普通版本?
答案 0 :(得分:3)
#include <utility>
#include <tuple>
#include <cstddef>
#include <type_traits>
template <typename T>
auto explode(T&& t, char)
{
return std::forward_as_tuple(std::forward<T>(t));
}
template <typename T, std::size_t I = std::tuple_size<std::decay_t<T>>{}>
auto explode(T&& t, int);
template <typename T, std::size_t... Is>
auto explode(T&& t, std::index_sequence<Is...>)
{
return std::tuple_cat(explode(std::get<Is>(std::forward<T>(t)), 0)...);
}
template <typename T, std::size_t I>
auto explode(T&& t, int)
{
return explode(std::forward<T>(t), std::make_index_sequence<I>{});
}
template <typename T, std::size_t... Is>
auto decay_tuple(T&& t, std::index_sequence<Is...>)
{
return std::make_tuple(std::get<Is>(std::forward<T>(t))...);
}
template <typename T>
auto decay_tuple(T&& t)
{
return decay_tuple(std::forward<T>(t), std::make_index_sequence<std::tuple_size<std::decay_t<T>>{}>{});
}
template <typename T, std::size_t... Is>
auto merge_tuple(T&& t, std::index_sequence<Is...>)
{
return decay_tuple(std::tuple_cat(explode(std::get<Is>(std::forward<T>(t)), 0)...));
}
template <typename T>
auto merge_tuple(T&& t)
{
return merge_tuple(std::forward<T>(t), std::make_index_sequence<std::tuple_size<std::decay_t<T>>{}>{});
}
测试:
int main()
{
std::tuple<
std::tuple<
std::tuple<
std::tuple<Noisy, Noisy>
>
>
, std::tuple<
std::tuple<Noisy>
>
> t;
auto x = merge_tuple(t);
static_assert(std::is_same<decltype(x), std::tuple<Noisy, Noisy, Noisy>>{}, "!");
}
输出:
Noisy()
Noisy()
Noisy()
Noisy(const Noisy&)
Noisy(const Noisy&)
Noisy(const Noisy&)
~Noisy()
~Noisy()
~Noisy()
~Noisy()
~Noisy()
~Noisy()
答案 1 :(得分:3)
这是我的看法。
首先,一个包装器,它需要正确处理包含源元组和元素的引用和cv限定的元组。我们存储1)这个包装器应该打开的类型; 2)我们是否可以从源头迁移。
template<class U, class T, bool can_move>
struct wrapper {
T* ptr;
wrapper(T& t) : ptr(std::addressof(t)) {}
using unwrapped_type =
std::conditional_t<can_move,
std::conditional_t<std::is_lvalue_reference<U>{}, T&, T&&>,
std::conditional_t<std::is_rvalue_reference<U>{}, T&&, T&>>;
using tuple_element_type = U;
unwrapped_type unwrap() const{
return std::forward<unwrapped_type>(*ptr);
}
};
接下来,打开一个包装元组的函数:
template<class... Wrappers, std::size_t... Is>
auto unwrap_tuple(const std::tuple<Wrappers...>& t, std::index_sequence<Is...>) {
return std::tuple<typename Wrappers::tuple_element_type...>(std::get<Is>(t).unwrap()...);
}
template<class... Wrappers>
auto unwrap_tuple(const std::tuple<Wrappers...>& t) {
return unwrap_tuple(t, std::index_sequence_for<Wrappers...>());
}
现在将一个(可能是嵌套的)元组转换为一个扁平的包装元组。这与Piotr的explode
非常相似:
template<bool can_move, class V, class T>
auto wrap_and_flatten(T& t, char){
return std::make_tuple(wrapper<V, T, can_move>(t));
}
template<class T> struct is_tuple : std::false_type {};
template<class... Ts> struct is_tuple<std::tuple<Ts...>> : std::true_type {};
template<class T> struct is_tuple<const T> : is_tuple<T> {};
template<class T> struct is_tuple<volatile T> : is_tuple<T> {};
template<bool can_move, class, class Tuple,
class = std::enable_if_t<is_tuple<std::decay_t<Tuple>>{}>>
auto wrap_and_flatten(Tuple& t, int);
template<bool can_move, class Tuple, std::size_t... Is>
auto wrap_and_flatten(Tuple& t, std::index_sequence<Is...>) {
return std::tuple_cat(wrap_and_flatten<can_move, std::tuple_element_t<Is, std::remove_cv_t<Tuple>>>(std::get<Is>(t), 0)...);
}
template<bool can_move, class V, class Tuple, class>
auto wrap_and_flatten(Tuple& t, int) {
using seq_type = std::make_index_sequence<std::tuple_size<Tuple>{}>;
return wrap_and_flatten<can_move>(t, seq_type());
}
template<class Tuple>
auto wrap_and_flatten_tuple(Tuple&& t){
constexpr bool can_move = !std::is_lvalue_reference<Tuple>{};
using seq_type = std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>{}>;
return wrap_and_flatten<can_move>(t, seq_type());
}
最后,将它们整合在一起 - 包裹,展平,然后展开:
template <typename T>
auto merge_tuple(T&& t)
{
return unwrap_tuple(wrap_and_flatten_tuple(std::forward<T>(t)));
}