Daniel Fray's answer の優れたアイデアに触発されて、これらの可変長特性の範囲を拡張することさえできます。タプルを使用すると、型の可変個パックを参照型と「のみ」比較するのではなく、可変型パックのコレクションに特性を適用できます。
たとえば、型int, int, int, float
が と同じ型であるかどうかを確認できますint, int, int, float
(実際にそうです!)。
そのためには、次の構成が必要です。
- タプルとタプルの末尾を生成する方法
- ブール値を追加 (または先頭に追加) してブール シーケンスを拡張する方法
TL;DR
この にいくつかの例をまとめましたlive demo。
variadic trait 機能の定義
最初に、bool シーケンスを一度に 1 つの値で拡張するヘルパーを提供します。
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 シーケンスをボトムアップ方式で再帰的に計算します。結果の 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>;
リストに特性ロジックを適用することもできます。たとえば、型の 2 つのリストが与えられた場合、最初のリストの型が 2 番目のリストの一致する型に変換可能かどうかを確認したい場合があります。
// 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;