受 Daniel Fray 回答中的绝妙想法的启发,我们甚至可以扩展这些可变参数特征的范围。使用元组,我们可以将特征应用于可变参数类型包的集合,而不是“仅”将可变参数类型包与引用类型进行比较。
例如,我们将能够查看类型是否与(确实是!)int, int, int, float
类型相同。int, int, int, float
为此,我们需要以下构造:
- 元组和产生元组尾部的方法
- 一种通过向布尔值附加(或预先附加)布尔值来扩展布尔序列的方法
TL;博士
我在此编译了一些示例live demo。
定义可变特征设施
首先,我们提供了一个帮助器来一次扩展一个布尔序列一个值:
template <bool ... Bs>
struct bool_sequence {};
template <bool b, typename T>
struct prepend_bool_seq;
template <bool b, bool ... bs>
struct prepend_bool_seq<b, bool_sequence<bs...>> {
typedef bool_sequence<b, bs...> type;
};
现在关于布尔序列的一些逻辑(取自其他答案)
template <typename T>
struct all_of;
template <bool ... Bs>
struct all_of<bool_sequence<Bs...>> :
public std::is_same<bool_sequence<true, Bs...>, bool_sequence<Bs..., true>> {};
template <typename T>
struct any_of;
template <bool ... Bs>
struct any_of<bool_sequence<Bs...>> :
public std::integral_constant<bool, !all_of<bool_sequence<!Bs...>>::value> {};
然后,我们定义一个辅助模板来访问元组的尾部:
namespace details {
// Sentinel type to detect empty tuple tails
struct null_type {};
template <typename T>
struct tuple_tail;
template <typename T>
struct tuple_tail<std::tuple<T>> {
typedef null_type type;
};
template <typename T, typename ... Ts>
struct tuple_tail<std::tuple<T, Ts...>> {
typedef std::tuple<Ts...> type;
};
}
组合结构
有了这些积木,我们现在可以定义一个apply_trait
模板来在几个类型列表上应用给定的类型特征:
namespace details {
template <template <typename...> class Trait, typename ... Tuples>
struct apply_trait {
static constexpr bool atomic_value =
Trait<typename std::tuple_element<0u, Tuples>::type...>::value;
typedef typename prepend_bool_seq<atomic_value,
typename apply_trait<Trait,
typename tuple_tail<Tuples>::type...>::type>::type type;
};
template <template <typename...> class Trait, typename ... Tuples>
struct apply_trait<Trait, null_type, Tuples...> {
typedef bool_sequence<> type;
};
}
该模板以自下而上的方式递归地计算特征应用程序给出的布尔序列。现在,提供了结果 bool 序列,我们可以使用上面定义的助手对结果执行逻辑运算。
are_same
接下来,一些助手可以为任何二元(或一元)类型特征重现您的示例逻辑:
// Helper templates for common type traits (unary and binary)
template <template <typename> class UnaryTrait, typename ... Ts>
using apply_unary_trait = details::apply_trait<UnaryTrait, std::tuple<Ts...>>;
template <template <typename, typename> class BinaryTrait, typename Ref, typename ... Ts>
using apply_binary_trait = details::apply_trait<BinaryTrait,
std::tuple<decltype(std::declval<Ts>(), std::declval<Ref>())...>,
std::tuple<Ts...>>;
template <template <typename, typename> class BinaryTrait, typename Ref, typename ... Ts>
using apply_binary_trait_ref_last = details::apply_trait<BinaryTrait,
std::tuple<Ts...>,
std::tuple<decltype(std::declval<Ts>(), std::declval<Ref>())...>>;
例如,我们可以重现are_same
您为每个二元特征提出的设计:
template <typename Ref, typename ... Ts>
using are_same = all_of<typename apply_binary_trait<std::is_same, Ref, Ts...>::type>;
我们还可以将特征逻辑应用于列表。例如,给定两个类型列表,我们可能想要检查第一个列表中的类型是否可以转换为第二个列表中的匹配类型:
// int is convertible to long and char const* is convertible to std::string
std::cout << all_of<details::apply_trait<std::is_convertible,
std::tuple<int, char const*>,
std::tuple<long, std::string>::type>::value;