递归模板参数删除
Recursive template argument removals
这个看似奇怪的问题旨在解决我似乎无法理解的一般类型的递归。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>>{});
}
相关文章:
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 如何在不强制转换每个参数的情况下删除初始值设定项列表中从 int 到 char 的缩小转换?
- std::p ackaged_task 应该删除带有 const 参数的复制 c'tor
- Qt5 - 如何将"QList<T> *"指针作为信号参数传递,并在完成后将其删除?
- 编译器在C++中调用另一个函数时,在参数中查找已删除的构造函数
- 从函数参数包中删除最后一项
- 为什么没有为函数参数删除副本
- 删除 Netbeans 中的标准运行参数
- 文档在哪里说明如果参数不是从末端删除参数,则无法从QT信号插槽连接中删除参数?
- 是否允许删除以修改其参数
- 如何通过对象参数从指针矢量中删除对象和指针
- 简化可变参数模板:删除一些专用项
- 为什么删除默认参数会破坏此 constexpr 计数器?
- 使用自定义参数的过载删除操作员
- 如何查找/删除具有特定参数的结构向量的元素
- 带大小参数和不带大小参数的"运算符删除":当两者都可用时,选择哪一个?
- 如何从可变参数模板参数中删除元素
- 为什么make_tuple删除参数类型的引用
- 删除参数中带有字符串的文件
- 使用std::bind删除参数