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)是为了支持某些具有begin()成员的模板库中的任何类型的容器,并且不需要容器定义一些value_type/iterator类型。据我所知,std::iterator_traits对于不使用对向 STL 公开其迭代器接口的容器,无法处理某种“密钥类型”,就像这样std::map做(例如:QMap<K,T>has value_type = T。您可以通过 . 访问密钥iterator::key())。

现在我想有条件地定义一个类型key_typeiifiterator有一个函数::key() const并采用它的返回类型,类似于我对value_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

您可以在类模板上使用 SFINAE 来创建一个基类模板,该模板定义key_type是否且仅当满足您需要的条件:

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_traitskey_type_definer类模板派生您的类模板:

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

range_type_traits现在将定义一个称为key_type当且仅当R具有成员函数的类型别名R key() const,其中R将是别名的类型key_type

于 2013-04-14T13:22:07.883 回答