6

次のように、メンバー関数の型を推測することにより、「範囲」(またはコンテナー、シーケンス) の型を定義する特性クラスがあります。

template<class R>
struct range_type_traits
{
    // "iterator": The type of the iterators of a range
    using iterator = decltype(std::begin(std::declval<R>()));

    // "value_type": The (non-reference) type of the values of a range
    using value_type = typename std::remove_reference<decltype(*(std::declval<iterator>()))>::type;
};

私がこれを行う理由 (およびR直接 orのサブタイプを使用しない) は、メンバーを持ち、コンテナーにいくつかの/型を定義する必要がないstd::iterator_traits、テンプレート化されたライブラリ内の任意の型のコンテナーをサポートするためです。私の知る限り、ペアを使用してイテレータ インターフェイスを STL に公開しないコンテナのある種の「キー タイプ」を処理することはできません(例: has . You can access the key via .)。begin()value_typeiteratorstd::iterator_traitsstd::mapQMap<K,T>value_type = Titerator::key()

ここで、 に関数がある場合に条件付きで型key_typeiifを定義し、その戻り値の型を取得します。既存の特性クラスに定義を入れるだけでは、これをサポートしていないコンテナーのコンパイルは失敗します。iterator::key() constvalue_type

SFINAE を使用するとstd::enable_if、テンプレート関数を条件付きで有効にすることができます。既存のクラスを条件付きで拡張する方法/条件付きでサブタイプを定義する方法は?

このスケッチのようなもの:

template<class R>
struct range_type_traits
{
    // "iterator": The type of the iterators of a range
    using iterator = decltype(std::begin(std::declval<R>()));

    // "value_type": The (non-reference) type of the values of a range
    using value_type = typename std::remove_reference<decltype(*(std::declval<iterator>()))>::type;

    ENABLE_IF_COMPILATION_DOES_NOT_FAIL {
        // "key_type": The (non-reference) type of the keys of an associative range not using pairs in its STL-interface
        using key_type = typename std::remove_reference<decltype(std::declval<iterator>().key())>::type;
    }
};
4

1 に答える 1

6

key_typeクラス テンプレートで SFINAE を使用して、必要な条件が満たされているかどうかを定義する基本クラス テンプレートを作成できます。

namespace detail
{

    // Primary template (does not define key_type)
    template<typename R, typename = void>
    struct key_type_definer { };

    // Specialization using SFINAE to check for the existence of key() const
    // (does define key_type)
    template<typename R>
    struct key_type_definer<
        R,
        typename std::enable_if<
            std::is_same<
                decltype(std::declval<R const>().key()),
                decltype(std::declval<R const>().key())
                >::value
            >::type
        >
    {
        // Type alias definition
        using key_type = typename std::remove_reference<
            decltype(std::declval<R const>().key())
            >::type;
    };

} // end namespace detail

range_traits次に、次のようにして、クラス テンプレートからクラス テンプレートを派生させることができますkey_type_definer

template<class R>
struct range_type_traits : detail::key_type_definer<R>
//                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
    // ...
};

range_type_traitsは、 がメンバー関数 を持っているkey_type場合にのみ呼び出される型エイリアスを定義します。ここで、は によってエイリアス化された型になります。RR key() constRkey_type

于 2013-04-14T13:22:07.883 に答える