19

はじめに

C++ の型特性を対応する可変個引数に変換するパターンを探しています。問題にアプローチするための方法論は高く評価され、タスクを自動化するためのジェネレーティブプログラミングパターンが理想的です。

次のようにします。

std::is_same<T, U>::value; 

私は次のように機能する特性を書きたい:

std::are_same<T1, T2, T3, T4>::value; 

現在のアプローチ

;を実装するのは非常に簡単です。一般的な解決策を求めて、普遍的な量化を実装する可変個の特性のツールを考え出すことができます:are_same

template<template<class,class> class F, typename...Ts>
struct Univ;

template<template<class, class> class F, typename T, typename U, typename...Ts>
struct Univ<F, T, U, Ts...>
{
    static const int value = F<T, U>::value && Univ<F, U, Ts...>::value;
};

template<template<class, class> class F, typename T>
struct Univ<F, T>
{
    static const int value = 1;
};

たとえば、次のように書くare_sameことができます

Univ<is_same,int, int, int>::value

are_classesこれは、are_scalarsなどの特性を作成するときに適用できます

一般化

マイナーな調整により、前のスニペットから存在の定量化&&が可能になり ( に置き換えられます) 、次の||ような特性を作成できます。exist_same

Exist<is_same, int, double, float>::value

質問

に関連する型特性に関する以前のカバーの一般化

  • プライマリ タイプ カテゴリ
  • 複合型カテゴリー
  • タイプ プロパティ
  • サポートされている操作

次のような型特性を一般化するにはどうすればよいですか。

    enable_if -> enable_if_any // enable if any clause is true
                 enable_if_all // enalbe if all clauses are true
                 enable_for    // enable only for the type provided

上記のexist_same例は単純化しすぎています。正しい実装のアイデアはありますか?

変更された型を「返す」type_traits があります。それらを任意の数のタイプの実装にスケーリングするための提案はありますか?

任意の数の型引数にスケーリングしないように作成されたtype_traits はありますか?

4

3 に答える 3

24

あなたが達成したいことを正確に理解していませんが、次のヘルパーが役立つ場合がありますbool_sequence

#include <type_traits>

// Note: std::integer_sequence is C++14,
// but it's easy to use your own version (even stripped down)
// for the following purpose:
template< bool... Bs >
using bool_sequence = std::integer_sequence< bool, Bs... >;

// Alternatively, not using C++14:
template< bool... > struct bool_sequence {};

次に、すべてまたはいずれかのブール値またはこれらで設定されているかどうかを確認できます。

template< bool... Bs >
using bool_and = std::is_same< bool_sequence< Bs... >,
                               bool_sequence< ( Bs || true )... > >;

template< bool... Bs >
using bool_or = std::integral_constant< bool, !bool_and< !Bs... >::value >;

それらは、より高度で特殊な特性の構成要素として役立ちます。たとえば、次のように使用できます。

typename< typename R, bool... Bs > // note: R first, no default :(
using enable_if_any = std::enable_if< bool_or< Bs... >::value, R >;

typename< typename R, bool... Bs > // note: R first, no default :(
using enable_if_all = std::enable_if< bool_and< Bs... >::value, R >;

typename< typename T, typename... Ts >
using are_same = bool_and< std::is_same< T, Ts >::value... >;
于 2014-07-10T22:12:48.720 に答える
1

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;
于 2016-07-27T14:57:07.133 に答える
1

std::conditionalまた、次のことを達成するためにenable_if_all使用することもできますenable_if_any

#include <type_traits>
#include <iostream>
#include <initializer_list>
#include <string>

namespace detail
{
template <typename... Conds>
struct and_ : std::true_type {};

template <typename... Conds>
struct or_ : std::false_type {};

template <typename Cond, typename... Conds>
struct and_<Cond, Conds...>
    : std::conditional<Cond::value, detail::and_<Conds...>, std::false_type>::type {};

template <typename Cond, typename... Conds>
struct or_<Cond, Conds...>
    : std::conditional<Cond::value, std::true_type, detail::and_<Conds...>>::type {};
}

template <typename... T>
using are_all_pod = detail::and_<std::is_pod<T>...>;

template <typename... T>
using any_is_pod = detail::or_<std::is_pod<T>...>;

template <typename... Args, typename = typename std::enable_if<are_all_pod<Args...>::value>::type>
void f(Args... args)
{
  (void)std::initializer_list<int>{(std::cout << args << '\n' , 0)...};
}

template <typename... Args, typename = typename std::enable_if<any_is_pod<Args...>::value>::type>
void g(Args... args)
{
  (void)std::initializer_list<int>{(std::cout << args << '\n' , 0)...};
}

int main()
{
  std::string s = "hello";  // non pod
  //f(1, 1.2, s); // this will fail because not all types are pod
  g(1, 1.2, s);   // this compiles because there is at least one pod in argument pack 
}
于 2016-07-08T15:47:46.880 に答える