问题描述
我正要问一个问题,但在写的时候我自己找到了答案。 问题是如何在给定模板模板谓词的情况下过滤元组而不对其进行专门化。
示例用法:
using tuple_list_t = std::tuple<std::string,int,none,double,char,abc,bool>;
using tuple_found_expected_t = std::tuple<int,bool>;
// filtering a tuple
using tuple_filtered_t = filter_t<tuple_list_t,std::is_fundamental>;
static_assert(std::is_same<tuple_filtered_t,tuple_found_expected_t>(),"");
如果有人对问题有任何建议或意见,请写下答案或评论。
解决方法
抱歉,您的解决方案在我看来过于复杂。特别是关于 SFINAE 部分(为什么是 void
?)。
简单的如下?
template <typename,template <typename...> class>
struct filter;
template <typename ... Ts,template <typename...> class Pred>
struct filter<std::tuple<Ts...>,Pred>
{
using type = decltype(std::tuple_cat(std::declval<
std::conditional_t<Pred<Ts>::value,std::tuple<Ts>,std::tuple<>>>()...));
};
template <typename Tpl,template <typename...> class Pred>
using filter_t = typename filter<Tpl,Pred>::type;
,
这是一个模板 SFINAE 结构的版本,用于过滤给定 SFINAE 谓词的 std::tuple
类型。所以调用看起来就像使用带有一元谓词的 STL 算法。
示例:
using tuple_list_t = std::tuple<std::string,int,none,double,char,abc,bool>;
using tuple_found_expected_t = std::tuple<int,bool>;
// filtering a tuple
using tuple_filtered_t = filter_t<tuple_list_t,std::is_fundamental>;
static_assert(std::is_same<tuple_filtered_t,tuple_found_expected_t>(),"");
我遇到了两个主要问题。
- 如何将模板作为模板模板参数传递而不对其进行专门化:
eld::filter<tuple,std::is_fundamental>
。这里的std::is_fundamental
是一个模板本身,需要专门化。但是我不能在调用之前对其进行专门化 - 我稍后需要使用元组中的类型对其进行专门化。
解决方法很简单。只需有一个存根void
(例如)默认专业化:
template <typename Tuple,template <typename> class SFINAEPredicate,typename = void>
- 使用
SFINAEPredicate<T>::value
删除产生 false 的类型。
现在有一种使用递归 SFINAE 模板特化的方法,但它需要大量输入,并且可能需要大量冗余代码生成。在调试中编译时,后者是一个大问题,因为 MinGW 会因汇编器错误file too big
而失败,到目前为止我还没有找到规避这个问题的可行方法(优化编译指示不做任何事情)。但这超出了这个问题/答案的范围。
我的做法是使用using type = decltype(std::tuple_cat(append_if_t<std::tuple<>,Types,SFINAEPredicate>()...));
append_if_t
返回一个元组,在某些情况下该元组为空,然后将所有这些元组连接成带有std::tuple_cat
的最终元组。
因此,append_if_t
有 N 个特化可以被编译器重用,并且没有递归中间特化。
代码如下:
template<typename Tuple,typename T,template<typename> class /*SFINAEPredicate*/,typename = T>
struct append_if;
template<typename T,template<typename> class SFINAEPredicate,typename ... Types>
struct append_if<std::tuple<Types...>,T,SFINAEPredicate,T>
{
using type = typename std::conditional<SFINAEPredicate<T>::value,std::tuple<Types...,T>,std::tuple<Types...>>::type;
};
template<typename Tuple,typename = T>
using append_if_t = typename append_if<Tuple,SFINAEPredicate>::type;
// TODO: function to traverse a tuple and find a type (index) that can be used to initialize a POD element
// pass a template parameter (trait validator) that accepts a type
// template for find_if in tuple
template<typename Tuple,typename = void>
struct filter;
template<template<typename> class SFINAEPredicate,typename ... Types>
struct filter<std::tuple<Types...>,void>
{
using type = decltype(std::tuple_cat(append_if_t<std::tuple<>,SFINAEPredicate>()...));
};
template<typename Tuple,typename = void>
using filter_t = typename filter<Tuple,SFINAEPredicate>::type;