递归模板参数删除

Recursive template argument removals

本文关键字:删除 参数 递归      更新时间:2023-10-16

这个看似奇怪的问题旨在解决我似乎无法理解的一般类型的递归。remove_unit_packs<Pack>::type应为Pack,去掉其内的所有单元包,其中单元包被定义为P<T>形式的任何包(包中只有一个元素,尽管该元素本身也可以是一个包)。因此

remove_unit_packs< std::tuple<int, P<float>, char> >::type

应为

std::tuple<int, char>

以下是我的实现,它使用递归:

#include <type_traits>
#include <tuple>
template <typename T>
struct is_unit_pack : std::false_type { };
template <template <typename> class P, typename T>
struct is_unit_pack<P<T>> : std::true_type { };
template <typename T, typename Output = std::tuple<>>
struct remove_unit_packs { using type = T; };
template <template <typename...> class P, typename... Output>
struct remove_unit_packs<P<>, std::tuple<Output...>> {
using type = P<Output...>;
};
template <template <typename...> class P, typename First, typename... Rest, typename... Output>
struct remove_unit_packs<P<First, Rest...>, std::tuple<Output...>> : std::conditional_t<is_unit_pack<First>::value,
remove_unit_packs<P<Rest...>, std::tuple<Output...>>,
remove_unit_packs<P<Rest...>, std::tuple<Output..., typename remove_unit_packs<First>::type>>  // We use 'typename remove_unit_packs<First>::type>' instead of simply 'First' in case 'First' contains a unit pack despite not being a unit pack (in which case all unit packs must be removed from within 'First').
> { };
// Test
template <typename...> struct P;
int main() {
static_assert(std::is_same<
remove_unit_packs< std::tuple<int, P<float>, char> >::type,
std::tuple<int, char>
>::value);
static_assert(std::is_same<
remove_unit_packs< std::tuple<int, P<float, P<P<P<void>>, bool, int>, long>, char> >::type,
std::tuple<int, P<float, P<bool, int>, long>, char>
>::value);
}

然而,请注意,作为另一个例子,

remove_unit_packs< std::tuple<int, P<float, P<bool>>, char> >::type

std::tuple<int, P<float>, char>

但这种输出类型也包含一个单独的包(P<float>),我也想将其删除,即继续删除所有单元包,直到任何地方都没有单元包为止(除非在特殊情况下,原始包已成为单元包,在这种情况下保持原样)。但我上面的代码并没有做到这一点,因为它只执行一次删除。我可以调用两次通行证来完成任务,但在某些情况下,需要3次通行证,或者谁知道有多少次通行证才能完全移除所有单元包。但我一直在想怎么做。有人能在这里提出一些想法吗?这些想法有望解决这类事情的一般做法?

更新:

我想我有个主意:

更改我的

template <template <typename...> class P, typename... Output>
struct remove_unit_packs<P<>, std::tuple<Output...>> {
using type = P<Output...>;
};

从上方到

template <template <typename...> class P, typename... Output>
struct remove_unit_packs<P<>, std::tuple<Output...>> {
using result = P<Output...>;
using type = std::conditional_t<contains_unit_pack<result>::value,
typename remove_unit_packs<result>::type,   
result
>;
};

其中CCD_ 5 I将定义。我的编译器目前抱怨循环逻辑之类的。如果有人认为这行不通,或者可能有更好的方法,请告诉我。

不确定你到底想要什么,但在我看来,你必须制作递归is_unit_pack

类似的东西

template <typename T>
struct is_unit_pack : std::false_type
{ };
template <template <typename...> class C, typename ... Ts>
struct is_unit_pack<C<Ts...>>
: std::integral_constant<bool,
(2 > sumI<(false == is_unit_pack<Ts>{})...>::value)>
{ };

其中sumI是可变整数的和

template <int ...>
struct sumI
{ static constexpr int value { 0 }; };
template <int I, int ... Is>
struct sumI<I, Is...>
{ static constexpr int value { I + sumI<Is...>::value }; };

以下是的完整编译示例

#include <tuple>
#include <type_traits>

template <typename...>
struct P
{ };
template <int ...>
struct sumI
{ static constexpr int value { 0 }; };
template <int I, int ... Is>
struct sumI<I, Is...>
{ static constexpr int value { I + sumI<Is...>::value }; };
template <typename T>
struct is_unit_pack : std::false_type
{ };
template <template <typename...> class C, typename ... Ts>
struct is_unit_pack<C<Ts...>>
: std::integral_constant<bool,
(2 > sumI<(false == is_unit_pack<Ts>{})...>::value)>
{ };
template <typename T, typename Output = std::tuple<>>
struct rupH
{ using type = T; };
template <template <typename...> class C, typename First, typename... Rest,
typename... Output>
struct rupH<C<First, Rest...>, std::tuple<Output...>>
: rupH<C<Rest...>, typename std::conditional<is_unit_pack<First>{},
std::tuple<Output...>,
std::tuple<Output..., typename rupH<First>::type>>::type>
{ };
template <template <typename...> class C, typename... Output>
struct rupH<C<>, std::tuple<Output...>>
{ using type = C<Output...>; };
template <typename T>
using rupH_t = typename rupH<T>::type;
template <typename T>
struct remove_unit_packs
{ using type = T; };
template <template <typename ...> class C, typename ... Ts>
struct remove_unit_packs<C<Ts...>>
{ using type = rupH_t<C<Ts...>>; };
template <typename T>
using remove_unit_packs_t = typename remove_unit_packs<T>::type;
int main ()
{
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, long>>,
std::tuple<int, long>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int>>,
std::tuple<int>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<long>, long>>,
std::tuple<int, long>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<P<long>>, long>>,
std::tuple<int, long>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<float, P<bool>>, char>>,
std::tuple<int, char>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<float, P<char>, bool>, char>>,
std::tuple<int, P<float, bool>, char>>{}, "!");
}

如果可以使用C++17,则不需要sumI,可以使用fold表达式;is_unit_pack变成简单的

template <typename T>
struct is_unit_pack : std::false_type
{ };
template <template <typename...> class C, typename ... Ts>
struct is_unit_pack<C<Ts...>>
: std::integral_constant<bool,
(2 > ((false == is_unit_pack<Ts>{}) + ...))>
{ };

在C++17中,去掉"广度递归"很简单。因此,只有"深度递归"。基于max66答案的is_unit_type特征:

#include <array>
#include <tuple>
#include <type_traits>
template<class...> struct P {};
// same as in [max66's answer](https://stackoverflow.com/a/46985839/2615118)
template <typename T>
struct is_unit_pack : std::false_type
{ };
template <template <typename...> class C, typename ... Ts>
struct is_unit_pack<C<Ts...>>
: std::integral_constant<bool,
(2 > ((false == is_unit_pack<Ts>{}) + ...))>
{ };
template<template<class> class Pred, class... Ts>
constexpr size_t count_if() {
return (size_t(0) + ... + Pred<Ts>{});
}
template<template<class> class Pred, class... Ts>
constexpr auto which() {
constexpr auto flags = std::array{bool(Pred<Ts>{})...};
constexpr size_t osize = count_if<Pred, Ts...>();
auto inds = std::array<size_t, osize>{};
auto it = inds.begin();
for(size_t i=0; i<flags.size(); ++i) if(flags[i]) *it++ = i;
return inds;
}
template<template<class> class Pred, class Ts, class Is>
struct keep_rec_if_impl;
template<template<class> class Pred, class T>
struct keep_rec_if {// non-specialized version for non-packs
using R = T;// break the depth recursion (see below)
};
template<template<class> class Pred, class... Ts>
struct keep_rec_if<Pred, P<Ts...>> {// specialized version for packs
static constexpr size_t osize = count_if<Pred, Ts...>();
using IndSeq = std::make_index_sequence<osize>;
using R = typename keep_rec_if_impl<Pred, P<Ts...>, IndSeq>::R;
};
template<template<class> class Pred, class... Ts>
using keep_rec_if_t = typename keep_rec_if<Pred, Ts...>::R;
template<template<class> class Pred, class... Ts, size_t... is>
struct keep_rec_if_impl<Pred, P<Ts...>, std::index_sequence<is...>> {
static constexpr auto inds = which<Pred, Ts...>();
template<size_t i> using T = std::tuple_element_t<inds[i], std::tuple<Ts...>>;
using R = P<keep_rec_if_t<Pred, T<is>>...>;// depth-recursion only
};
template<template<class> class Pred, class Ts>
struct remove_rec_if {
template<class U>
using NotPred = std::integral_constant<bool, !Pred<U>{}>;
using R = keep_rec_if_t<NotPred, Ts>;
};
template<template<class> class Pred, class Ts>
using remove_rec_if_t = typename remove_rec_if<Pred, Ts>::R;
template<class Ts>
using remove_unit_packs_t = remove_rec_if_t<is_unit_pack, Ts>;
int main () {
static_assert(std::is_same<
remove_unit_packs_t<P<int, long>>,
P<int, long>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<P<int>>,
P<int>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<P<int, P<long>, long>>,
P<int, long>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<P<int, P<P<long>>, long>>,
P<int, long>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<P<int, P<float, P<bool>>, char>>,
P<int, char>>{}, "!");
static_assert(std::is_same<
remove_unit_packs_t<P<int, P<float, P<char>, bool>, char>>,
P<int, P<float, bool>, char>>{}, "!");
return 0;
}

我的方法有效,但我必须接受max66更原始的解决方案。

#include <type_traits>
#include <tuple>
namespace detail {
template <bool...> struct any_of;
template <>
struct any_of<> : std::false_type {};
template <bool... Bs>
struct any_of<true, Bs...> : std::true_type {};
template <bool First, bool... Rest>
struct any_of<First, Rest...> : any_of<Rest...> {};
}
template <typename T>
struct is_unit_pack : std::false_type {};
template <template <typename> class P, typename T>
struct is_unit_pack<P<T>> : std::true_type {};
template <typename T> struct contains_unit_pack : std::false_type {};
template <template <typename...> class P, typename... Ts>
struct contains_unit_pack<P<Ts...>> : 
detail::any_of<(is_unit_pack<Ts>::value || contains_unit_pack<Ts>::value)...> {};
// remove_unit_packs
template <typename T, typename Output = std::tuple<>>
struct remove_unit_packs {
using type = T;
};
template <typename Result, bool>
struct check {
using type = Result;    
};
template <typename Result>
struct check<Result, true> : remove_unit_packs<Result> {};  // The key recursive call.
template <template <typename...> class P, typename... Output>
struct remove_unit_packs<P<>, std::tuple<Output...>> :
check<P<Output...>, contains_unit_pack<P<Output...>>::value> {};
template <template <typename...> class P, typename First, typename... Rest, typename... Output>
struct remove_unit_packs<P<First, Rest...>, std::tuple<Output...>> : std::conditional_t<is_unit_pack<First>::value,
remove_unit_packs<P<Rest...>, std::tuple<Output...>>,
remove_unit_packs<P<Rest...>, std::tuple<Output..., typename remove_unit_packs<First>::type>>  // We use 'typename remove_unit_packs<First>::type>' instead of simply 'First' in case 'First' contains a unit pack despite not being a unit pack (in which case all unit packs must be removed from within 'First').
> {};
template <typename T>
using remove_unit_packs_t = typename remove_unit_packs<T>::type;
// Test
template <typename...> struct P;
int main() {
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<>>>,
std::tuple<int, P<>>>{});
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int>>,
std::tuple<int>>{});
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<long>, long>>,
std::tuple<int, long>>{});
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<P<long>>, long>>,
std::tuple<int, long>>{});
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<float, P<bool>>, char>>,
std::tuple<int, char>>{});
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<float, P<char>, bool>, char>>,
std::tuple<int, P<float, bool>, char>>{});
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<float, P<char>, bool>, char>>,
std::tuple<int, P<float, bool>, char>>{});
static_assert(std::is_same<
remove_unit_packs_t<std::tuple<int, P<P<float, P<bool>, P<long>>, int, P<void>>, P<P<char, bool>>>>,
std::tuple<int>>{});
}