template <typename Pack, typename T, std::size_t... Is> struct remove_some
从T
Pack
中删除Is...
个template <typename...> struct P; template <typename...> struct Q;
static_assert (std::is_same<
remove_some<std::tuple<int, char, bool, int, int, double, int>, int, 1,2>,
std::tuple<int, char, bool, double, int>
>::value, "");
static_assert (std::is_same<
remove_some<std::tuple<int, char, bool, int, int, double, int>, int, 0,3>,
std::tuple<char, bool, int, int, double>
>::value, "");
static_assert (std::is_same<
remove_some<std::tuple<int, char, P<long, int, short>, bool, int, int, double, int>,
int, 0,1,2,4>,
std::tuple<char, P<long, short>, bool, int, double>
>::value, "");
。例如:
static_assert (std::is_same< // Fails
remove_some<std::tuple<int, char, P<long, int, Q<int, int, int>, short>, bool, int, int, double, int>, int, 0,1,2>,
std::tuple<char, P<long, Q<int, int>, short>, bool, int, int, double, int>
>::value, "");
这些断言都是通过我当前的代码传递的,但问题是让这个断言通过:
#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
template <typename Pack, typename T, std::size_t Count, typename Output, std::size_t... Is> struct remove_some_h;
template <typename Pack, typename T, std::size_t... Is>
using remove_some = typename remove_some_h<Pack, T, 0, std::tuple<>, Is...>::type;
template <template <typename...> class P, typename First, typename... Rest, typename T, std::size_t Count, typename... Output, std::size_t I, std::size_t... Is>
struct remove_some_h<P<First, Rest...>, T, Count, std::tuple<Output...>, I, Is...> : remove_some_h<P<Rest...>, T, Count, std::tuple<Output..., First>, I, Is...> {};
// T is found, but it is not the Ith one, so do NOT remove it. Increase Count by 1 to handle the next T.
template <template <typename...> class P, typename... Rest, typename T, std::size_t Count, typename... Output, std::size_t I, std::size_t... Is>
struct remove_some_h<P<T, Rest...>, T, Count, std::tuple<Output...>, I, Is...> : remove_some_h<P<Rest...>, T, Count + 1, std::tuple<Output..., T>, I, Is...> {};
// T is found, and it is the next one to remove, so remove it and increase Count by 1 to handle the next T.
template <template <typename...> class P, typename... Rest, typename T, std::size_t Count, typename... Output, std::size_t... Is>
struct remove_some_h<P<T, Rest...>, T, Count, std::tuple<Output...>, Count, Is...> : remove_some_h<P<Rest...>, T, Count + 1, std::tuple<Output...>, Is...> {};
// No more indices left, so no more T's to remove and hence just adjoin Rest... to the output.
template <template <typename...> class P, typename... Rest, typename T, std::size_t Count, typename... Output>
struct remove_some_h<P<Rest...>, T, Count, std::tuple<Output...>> {
using type = P<Output..., Rest...>;
static constexpr std::size_t new_count = Count;
using remaining_indices = std::index_sequence<>;
};
// No more types left to check, though there are still some T's left to remove (e.g. from an outerpack that contains this inner pack).
template <template <typename...> class P, typename T, std::size_t Count, typename... Output, std::size_t... Is>
struct remove_some_h<P<>, T, Count, std::tuple<Output...>, Is...> {
using type = P<Output...>;
static constexpr std::size_t new_count = Count;
using remaining_indices = std::index_sequence<Is...>;
};
// No more types left to check, nor any T's left to remove (this is needed to avoid ambiguity).
template <template <typename...> class P, typename T, std::size_t Count, typename... Output>
struct remove_some_h<P<>, T, Count, std::tuple<Output...>> {
using type = P<Output...>;
static constexpr std::size_t new_count = Count;
using remaining_indices = std::index_sequence<>;
};
// The problem case (dealing with inner packs):
template <typename Pack, typename T, std::size_t Count, typename Output, typename IndexSequence> struct remove_some_h_index_sequence;
template <typename Pack, typename T, std::size_t Count, typename Output, std::size_t... Is>
struct remove_some_h_index_sequence<Pack, T, Count, Output, std::index_sequence<Is...>> : remove_some_h<Pack, T, Count, Output, Is...> {};
template <template <typename...> class P, template <typename...> class Q, typename... Ts, typename... Rest, typename T, std::size_t Count, typename... Output, std::size_t I, std::size_t... Is>
struct remove_some_h<P<Q<Ts...>, Rest...>, T, Count, std::tuple<Output...>, I, Is...> { // I is needed to avoid ambiguity.
static constexpr std::size_t new_count = Count; // I think this value is wrong?
using remaining_indices = std::index_sequence<I, Is...>; // I think this is the wrong sequence?
using inner = remove_some_h<Q<Ts...>, T, Count, std::tuple<>, I, Is...>; // Take care of the inner pack first.
using type = typename remove_some_h_index_sequence<P<Rest...>, T, inner::new_count, std::tuple<Output..., typename inner::type>, typename inner::remaining_indices>::type;
};
即。一包内的包装,我似乎无法确定失败的原因。这是我目前的代码,包括我认为错误的地方,但总是欢迎更好的方法。
{{1}}
答案 0 :(得分:2)
以下是您上一个remove_some_h
子句的修复:
template <template <typename...> class P, template <typename...> class Q,
typename... Ts, typename... Rest, typename T, std::size_t Count,
typename... Output, std::size_t I, std::size_t... Is>
struct remove_some_h<P<Q<Ts...>, Rest...>, T, Count,
std::tuple<Output...>, I, Is...> { // I is needed to avoid ambiguity.
using inner = remove_some_h<Q<Ts...>, T, Count, std::tuple<>, I, Is...>; // Take care of the inner pack first.
using removed = remove_some_h_index_sequence<
P<Rest...>, T, inner::new_count, std::tuple<Output...,
typename inner::type>,
typename inner::remaining_indices>;
using type = typename removed::type;
static constexpr auto new_count = removed::new_count;
using remaining_indices = typename removed::remaining_indices;
};
答案 1 :(得分:2)
你正在接近这个问题。你的原语以深度优先的方式遍历一个类型的树,计算给定类型的元素,并在某些索引处删除它们,这是一个荒谬的原语。从某种意义上说,它确实太多,而且太具体了。
相反,你应该看看现有的列表处理函数式语言如何解决这样的问题。他们构建原始操作并组合它们,而不是像你那样手写复杂的原语。
第一步是单位清单。你想走一个平面列表,在每个元素上运行一个谓词。如果谓词这样说,你想要消除元素。你也想要在你继续修改谓词的状态。
接下来,修改线性列表遍历器,使其成为树的深度优先遍历。这可以使用谓词mutator(一个接受谓词,并在访问参数后使其下降到参数内容中)来完成。
所以现在你正在对类型树进行深度优先遍历,通过一个过滤器来消除它的类型。
现在我们构建一个谓词“删除类型T
的第n个实例”。
-
关键是这些技术中的每一种都可以独立测试。
两种可能有用的方法是将模板视为类型(模板是一种带有template<class...Ts> using result=/*...*/;
的类型),或者去hana风格并在constexpr和decltype'd函数中进行元编程类型标签。
template<class T>struct tag_type{using type=T;};
template<class T>constexpr tag_type<T> tag{};
hana风格元编程的标签。
一些hana风格的过滤器:
struct filter_never {
template<class U>
constexpr std::pair<std::false_type, filter_never>
operator()(tag_type<U>){ return {}; }
};
template<class T, std::size_t N>
struct filter_nth {
template<class U>
constexpr std::pair< std::false_type, filter_nth<T, N> >
operator()(tag_type<U>)const{return {};}
constexpr std::pair< std::false_type, filter_nth<T, N-1> >
operator()(tag_type<T>)const{return {};}
};
template<class T>
struct filter_nth<T, 0> {
template<class U>
constexpr std::pair< std::false_type, filter_nth<T, 0> >
operator()(tag_type<U>)const{return {};}
constexpr std::pair< std::true_type, filter_never >
operator()(tag_type<T>)const{return {};}
};
过滤器兼并:
template<class...Filters>
struct filter_any:filter_never{};
template<class...>struct types_tag { using type=types_tag; };
template<class...Ts>
constexpr types_tag<Ts...> types{};
template<class...Truth, class...Filters>
constexpr auto merge_filter_results( std::pair<Truth, Filters>... )
-> std::pair<
std::integral_constant<bool, (Truth{} || ...)>, // C++1z, can write but long in C++11
filter_any<Filters...>
>
{ return {}; }
template<class F0, class...Filters>
struct filter_any<F0, Filters...> {
template<class U>
constexpr auto operator()(tag_type<U> t)const {
return merge_filter_results( F0{}(t), Filters{}(t)... );
}
};
filter_any<Filters...>
合并任意数量的过滤器,并将它们应用于每个元素。如果有人说“丢弃”,结果就会丢弃。
因此,int, 1, 2
变为filter_any<filter_nth<int, 1>, filter_nth<int, 2>>
。
这似乎很复杂;但重要的是我只是减少了从列表中删除多个元素的问题,以测试一次消除一个元素的能力,并测试filter_any
。两个组件,每个组件都经过单独测试。
template<class...T0s, class...T1s>
constexpr types_tag<T0s..., T1s...> concat_elements( types_tag<T0s...>, types_tag<T1s...> )
{ return {}; }
template<class Filter>
constexpr types_tag<> filter_elements( Filter, types_tag<> ) { return {}; }
template<class T0, class...Ts, class Filter>
auto filter_elements( Filter, types_tag<T0, Ts...> )
-> decltype(
concat_elements( std::conditional_t<
Filter{}(tag<T0>).first,
types_tag<>,
types_tag<T0>
>{},
filter_elements( Filter{}(tag<T0>).second, types_tag<Ts...> )
)
)
{ return {}; }
现在,除了错别字,我们可以:
auto r = filter_elements(
filter_any<filter_nth<int, 1>, filter_nth<int, 2>>{},
types_tag<int, char, char, std::string, char, int, char, int, int, char>{}
);
,r
的类型现在是
types_tag<int, char, char, std::string, char, char, int, char>
剩下要做的就是调试上面的废话,并处理下降。
我在types_tag
工作,因为采用任何模板类型并将其来回转录到types_tag
相对容易。我们在轻量级类型types_tag
中所做的工作越多,我们的工作速度就越快。
我们所需要的只是转录:
template<template<class...>class Z, class types>
struct transcribe;
template<template<class...>class Z, class types>
using transcribe_t=typename transcribe<Z,types>::type;
template<template<class...>class Z, class...Ts>
struct transcribe<Z,types_tag<Ts...>>:tag_type<Z<Ts...>> {};
并偷窃:
template<class T>
struct as_types;
template<class T>
using as_types_t=typename as_types<T>::type;
template<template<class...>class Z, class...Ts>
struct as_types<Z<Ts...>>:types_tag<Ts...>{};
从任意包裹中来回移动。
template<class T, class Filter>
struct filter_elements_out;
template<class T, class Filter>
using filter_elements_out_t=typename filter_elements_out<T,Filter>::type;
template<template<class...>class Z, class...Ts, class Filter>
struct filter_elements_out:
type_tag<
transcribe_t<Z,
decltype(filter_elements(Filter{},types<Ts...>{}))
>
>
{};
这很容易,没有?
我确实谈过适应深度优先过滤器。这需要更多的努力。
让函数返回types_tag<...>
而不是true / false,并且重新连接回原始列表会更好,因为事实证明。 (当我实施它时注意到这一点)。
以下是filter-&gt; polymap(返回集合的地图)和深度优先适配器:
template<class Filter>
struct filter_to_polymap {
template<class U>
constexpr
std::pair<
std::conditional_t<
Filter{}(tag<U>).first,
types_tag<>,
types_tag<U>
>,
filter_to_polymap<decltype(Filter{}(tag<U>).second)>
> operator()(tag_type<U>)const {
static_assert(
!std::is_same<decltype(Filter{}(tag<U>).second), filter_never>{}
,""
);
return {};
}
};
template<class Polymap>
constexpr std::pair<types_tag<>, Polymap> map_elements_ex( Polymap, types_tag<> ) { return {}; }
template<class Polymap, class T0, class...Ts>
constexpr auto map_elements_ex( Polymap p, types_tag<T0, Ts...> )
{
return std::make_pair(
concat_elements(
p(tag<T0>).first,
map_elements_ex( p(tag<T0>).second, types<Ts...> ).first
),
map_elements_ex( p(tag<T0>).second, types<Ts...> ).second
);
}
template<class Polymap, class...Ts>
auto map_elements( Polymap p, types_tag<Ts...> ) {
return map_elements_ex(p, types<Ts...>).first;
}
template<class Polymap, template<class...>class Z, class...Ts>
auto map_elements_tagged( Polymap, tag_type<Z<Ts...>> ) {
return tag< transcribe_t<Z, decltype(map_elements(Polymap{}, types<Ts...>))> >;
}
template<class Polymap>
struct depth_first_polymap {
template<template<class...>class Z, class...Ts>
constexpr auto operator()(tag_type<Z<Ts...>>) const {
auto r = map_elements_ex(*this, types<Ts...>);
return std::make_pair(
types<transcribe_t<Z, decltype(r.first)>>,
r.second
);
}
template<class U>
constexpr auto operator()(tag_type<U>) const {
auto r=Polymap{}( tag<U> );
return std::make_pair(
r.first,
depth_first_polymap<decltype(r.second)>{}
);
}
};