【问题标题】:Extending enable_if type to exclude a matching type扩展 enable_if 类型以排除匹配类型
【发布时间】:2014-01-06 02:28:21
【问题描述】:

我正在使用this 神奇的标头来获得轻松序列化 STL 容器的能力。

然而,我现在已经开始为我的类型使用更花哨的 HTML 序列化程序,我想做的一部分是将operator << 功能推广到我的新类型ohtmlstringstream,它由@987654325 支持@。

这是我(正在运行的)尝试这样做(ohtmlstringstream::write 是一个公共模板方法,它将其 arg 传递给私有成员 stringstreamoperator<<):

namespace std {
    template<typename T>
    inline typename enable_if< ::pretty_print::is_container<T>::value, ohtmlstringstream&>::type
    operator<<(ohtmlstringstream& os, const T& container) {
        auto it = std::begin(container);
        const auto the_end = end(container);
        os.write("<div class='container'>");
        while(it != the_end) {
            os << *it;
            it++;
        }
        os.write("</div>");
        return os;
    }
}

我遇到的第一个问题是,任何时候在ohtmlstringstream 上使用std::string,它都会被视为一个容器,这是我不想要的;我想将字符串视为字符串,而不是容器。当然,就pretty_print 而言,std::string 肯定是一个字符容器。

这是prettyprint.hpp的摘录:

namespace pretty_print
{

    // SFINAE type trait to detect whether T::const_iterator exists.

    template<typename T>
    struct has_const_iterator
    {
    private:
        typedef char                      yes;
        typedef struct { char array[2]; } no;

        template <typename C> static yes test(typename C::const_iterator*);
        template <typename C> static no  test(...);
    public:
        static const bool value = sizeof(test<T>(0)) == sizeof(yes);
        typedef T type;
    };

    // SFINAE type trait to detect whether "T::const_iterator T::begin/end() const" exist.

    template <typename T>
    struct has_begin_end_OLD
    {
        struct Dummy { typedef void const_iterator; };
        typedef typename std::conditional<has_const_iterator<T>::value, T, Dummy>::type TType;
        typedef typename TType::const_iterator iter;

        struct Fallback { iter begin() const; iter end() const; };
        struct Derived : TType, Fallback { };

        template<typename C, C> struct ChT;

        template<typename C> static char (&f(ChT<iter (Fallback::*)() const, &C::begin>*))[1];
        template<typename C> static char (&f(...))[2];
        template<typename C> static char (&g(ChT<iter (Fallback::*)() const, &C::end>*))[1];
        template<typename C> static char (&g(...))[2];

        static bool const beg_value = sizeof(f<Derived>(0)) == 2;
        static bool const end_value = sizeof(g<Derived>(0)) == 2;
    };

    template <typename T>
    struct has_begin_end
    {
        template<typename C> static char (&f(typename std::enable_if<
                                             std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::begin)),
                                             typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

        template<typename C> static char (&f(...))[2];

        template<typename C> static char (&g(typename std::enable_if<
                                             std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::end)),
                                             typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

        template<typename C> static char (&g(...))[2];

        static bool const beg_value = sizeof(f<T>(0)) == 1;
        static bool const end_value = sizeof(g<T>(0)) == 1;
    };

    // Basic is_container template; specialize to derive from std::true_type for all desired container types

    template<typename T> struct is_container : public ::std::integral_constant<bool,
    has_const_iterator<T>::value && has_begin_end<T>::beg_value && has_begin_end<T>::end_value> { };

    template<typename T, std::size_t N> struct is_container<T[N]> : public ::std::true_type { };

    template<std::size_t N> struct is_container<char[N]> : public ::std::false_type { };

    template <typename T> struct is_container< ::std::valarray<T>> : public ::std::true_type { }; 

...<snip>

这里的问题是我不清楚如何使用 SFINAE 和 enable_if 以及其余的东西来构建另一个谓词,该谓词对所有容器都评估为 true 除了 @987654336 @。

现在,这只是第一个问题。第二个问题是我的第一个代码清单中的os.write("&lt;div class='container'&gt;"); 行。请注意这是多么令人讨厌的不确定性。我真的希望容器的序列化例程能够报告容器的实际类型(无论是std::mapstd::forward-list 还是std::vector)。

我想知道的是是否存在一些(相当理智的)方法来使用模板实现这一点,或者我是否真的应该只使用宏来显式定义一系列模板,每个 STL 容器类型都有一个:这样我就可以轻松地为任何给定的容器构建我想要的确切类型的 HTML。

确实,使用模板枚举所有 STL 容器可以解决这两个问题。我想我会开始这样做。但我仍然想知道最初的第一个问题的答案。如何使用enable_if 排除特定类型?

【问题讨论】:

  • I'd like to treat strings as just strings, not as containers 什么
  • ::pretty_print::is_container&lt;std::string&gt;::value == true。它有一个const_iterator,它有开始和结束。不过我也有点想知道has_begin_end到底在做什么以及它为什么起作用。
  • @StevenLu,在 SFINAE 部分不是那么好,我可以告诉你f 是一个返回对 N 个字符数组的引用的函数。它在参数类型中使用 SFINAE。如果成员不存在,我想它的工作方式与在底部的调用中不考虑复杂的方法相同。我不确定它是否必须在参数中,但 decltype in the return type 在 imo 中更具可读性。我想这也可以确保它也是正确类型的函数。
  • 您不需要也绝对不允许在operator&lt;&lt; 中声明namespace std 重载。

标签: c++ c++11 stl sfinae


【解决方案1】:

&amp;&amp;!std::is_same&lt;Foo, std::string&gt;::value 添加到您的enable_if 测试中。

enable_if&lt; 条件, 可选类型&gt;::type 的语法可能会有所帮助——条件是any 编译时间bool。你可以往里面扔更多的东西!

如果您想要一个 trait 以供重用,只需在此处创建一个继承自 std::integral_constant&lt;bool, 逻辑 &gt;{}; 的 trait。

如果您有完整的 C++11 支持,请尝试编写一个 constexpr 函数,该函数采用 T 类型并返回 true 而不是特征类。在 C++1y 中,当概念 lite 出现时,这可能很有用。

【讨论】:

  • 处理第二个问题有什么技巧吗?我想我可以写更多的部分模板特化之类的?
  • @stevenlu 只需编写一个快速特征类,它有一个返回容器名称的静态方法。要么保留未实现的非专业特征以获取错误,要么给它一个通用名称。这与代码的其余部分正交...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-07
  • 2019-08-10
  • 2018-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多