【问题标题】:Conditionally enable a sub-type (similar to enable_if to enable functions)有条件地启用一个子类型(类似于 enable_if 启用功能)
【发布时间】:2013-04-14 13:12:32
【问题描述】:

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

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 所做的(例如:QMap&lt;K,T&gt; 具有 value_type = T . 您可以通过iterator::key() 访问密钥。)。

现在我想有条件地定义一个类型 key_type iif iterator 有一个函数 ::key() const 并采用它的返回类型,类似于我对 value_type 所做的事情。如果我只是将定义放在现有的特征类中,那么不支持它的容器编译会失败。

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

类似这样的草图:

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

【问题讨论】:

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


    【解决方案1】:

    您可以在类模板上使用 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
    

    然后,您可以从key_type_definer 类模板派生您的range_traits 类模板,这样:

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

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

    【讨论】:

    • 给我一秒钟试试这个。可能需要一段时间,我只是在学习类型特征、SFINAE、enable_if 等。提前致谢。 ;)
    • 它似乎有效(在将继承中的 R 更改为 R 的迭代器类型之后,因为 key() 是在迭代器上定义的,而不是在 R 本身上)。但我不明白为什么我的解决方案失败了。让我向您展示我的方法,也许您可​​以告诉我出了什么问题:ideone.com/LJnRQ3 -- 关于如何“调试”这些东西的任何提示?如果你知道我的意思的话,在它起作用之前猜测事情真的很糟糕......
    • 不错的答案,像往常一样。我只想提一个简化。用template&lt;typename R, typename = int&gt; struct key_type_definer { }; 定义主模板,用template&lt;typename R&gt; struct key_type_definer&lt;R, decltype(std::declval&lt;R const&gt;().key(), 0)&gt; { /* same as before */ } 定义专业化模板。很抱歉在评论中放了“很多”代码,但我认为这不值得单独回答。
    • 另外,我不明白为什么我在超类range_basic_type_traits 中定义的子类型不可用。我记得如果我访问子类型,我必须在前面加上 typename,但不是说我必须在前面加上超类名称。
    • 我的最终解决方案:ideone.com/DyoebO -- 谢谢你们两位,也感谢@CassioNeri,感谢新的 decltype,没有 enable_if 的语法更简洁。很不错。 ;)
    猜你喜欢
    • 2020-09-29
    • 1970-01-01
    • 1970-01-01
    • 2018-02-10
    • 2023-01-20
    • 1970-01-01
    • 1970-01-01
    • 2014-10-24
    • 1970-01-01
    相关资源
    最近更新 更多