有条件地启用子类型(类似于enable_if以启用函数)

时间:2013-04-14 13:12:32

标签: c++ templates c++11 sfinae typetraits

我有一个traits类,它通过推导成员函数的类型来定义“范围”(或容器,序列)的类型,如下所示:

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;
};

我这样做的原因(并且不直接使用Rstd::iterator_traits的子类型)是为了支持某个模板化库中具有begin()成员的任何类型的容器不要求容器定义了一些value_type / iterator类型。据我所知,std::iterator_traits无法处理某些容器的“密钥类型”,这些容器没有使用对象将它们的迭代器接口暴露给STL,例如std::map(例如:{{1} {}有QMap<K,T>。您可以通过iterator::key()访问密钥。)。

现在我想有条件地定义一个类型value_type = T如果key_type有一个函数iterator并采用它的返回类型,类似于我对::key() const的处理方式。如果我只是将定义放在现有的traits类中,那么对于不支持此功能的容器,编译就会失败。

value_type的SFINAE可以有条件地启用模板功能。如何有条件地扩展现有类/有条件地定义子类型?

像这样的草图:

std::enable_if

1 个答案:

答案 0 :(得分: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_traits类模板派生key_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