【问题标题】:detecting typedef at compile time (template metaprogramming)在编译时检测 typedef(模板元编程)
【发布时间】:2011-10-20 09:59:48
【问题描述】:

我目前正在做一些模板元编程。就我而言,我可以处理任何“可迭代”类型,即typedef foo const_iterator 以相同方式存在的任何类型。我试图为此使用新的 C++11 模板元编程,但是我找不到检测某个类型是否丢失的方法。

因为我还需要根据其他特性打开/关闭其他模板特化,所以我目前使用的模板有两个参数,第二个是通过std::enable_if 生成的。这是我目前正在做的事情:

template <typename T, typename Enable = void>
struct Foo{}; // default case is invalid

template <typename T>
struct Foo< T, typename std::enable_if<std::is_fundamental<T>::value>::type>{ 
   void do_stuff(){ ... }
};

template<typename T>
struct exists{
   static const bool value = true;
};

template<typename T>
struct Foo<T, typename std::enable_if<exists< typename T::const_iterator >::value >::type> {
    void do_stuff(){ ... }
};

如果没有 exists 帮助器模板,我无法做这样的事情。例如简单地做

template<typename T>
struct Foo<T, typename T::const_iterator> {
    void do_stuff(){ ... }
};

不起作用,因为在应该使用这种特殊化的情况下,无效的默认情况被实例化了。

但是,我在新的 C++11 标准中的任何地方都找不到这个 exists,据我所知,它只是从 boost::type_traits 获取这类东西。然而,在homepage 上,boost::type_traits 并没有显示任何可以替代使用的引用。

是否缺少此功能,或者我是否忽略了其他一些明显的方法来实现所需的行为?

【问题讨论】:

    标签: c++ c++11 sfinae template-meta-programming typetraits


    【解决方案1】:

    如果您只是想要给定类型是否包含const_iterator,那么以下是您的代码的简化版本:

    template<typename T>
    struct void_ { typedef void type; };
    
    template<typename T, typename = void>
    struct Foo {};
    
    template<typename T>
    struct Foo <T, typename void_<typename T::const_iterator>::type> {
          void do_stuff(){ ... }
    };
    

    请参阅 this answer 了解有关此技术如何工作的一些说明。

    【讨论】:

    • 您或许应该发布一个链接,指向您关于这个如何工作的问题。 :) 另外,您似乎很喜欢这个,已经看到您多次推荐它了。
    • @Xeo,是的,这非常简单直接。但我没有得到你的第一部分You should maybe post a link to your questions on how this one works. :) ...你的意思是在回答时我应该把我以前的问题的链接(而不是代码本身)?我怀疑在 SO 上不建议这样做。
    • Nono,我的意思是,你发布了一个指向your question where you asked how this works 的链接,因为一开始它并不是很明显。糟糕,我刚刚注意到我写了“问题”,当然只意味着一个问题。
    • @Xeo,实际上你是正确的questions。因为我最近的问题仅与此机制有些相关。所以,现在我已经掌握了它。
    • @xiaodong,基本上当你有SFINAE时,屈服类型应该是一样的。例如,在这种情况下,“决定类型”是void,它用作默认struct Foo 的第二个默认类型。现在Foo 的所有后续特化必须具有相同的第二类型。为此,我们需要void_ 机制。另请参阅上面 Xeo 提供的链接。我问过和你一样的问题:Different template syntax for finding if argument is a class or not.
    【解决方案2】:

    您可以创建一个特征has_const_iterator,它提供一个布尔值并在特化中使用它。

    这样的事情可能会做到:

    template <typename T>
    struct has_const_iterator {
    private:
        template <typename T1>
        static typename T1::const_iterator test(int);
        template <typename>
        static void test(...);
    public:
        enum { value = !std::is_void<decltype(test<T>(0))>::value };
    };
    

    然后你可以像这样专门化:

    template <typename T,
              bool IsFundamental = std::is_fundamental<T>::value,
              bool HasConstIterator = has_const_iterator<T>::value>
    struct Foo; // default case is invalid, so no definition!
    
    template <typename T>
    struct Foo< T, true, false>{ 
       void do_stuff(){// bla }
    };
    
    template<typename T>
    struct Foo<T, false, true> {
        void do_stuff(){//bla}
    };
    

    【讨论】:

    • 您选择test(int) 而不是test() 是否有原因?只要确保我了解重载解决方案在这里的工作原理。谢谢!
    • @nknight: 原因:拨打test 明确。
    【解决方案3】:

    这是成员类型特征检查的另一个版本:

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

    【讨论】:

      【解决方案4】:

      有几种方法可以做到这一点。在 C++03 中,您可以使用 boost 和 enable_if 来定义特征(docssource):

      BOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator);
      
      template <typename T, typename Enable = void>
      struct Foo;
      
      template <typename T>
      struct Foo< T, typename boost::enable_if<boost::is_fundamental<T> >::type>{ 
         void do_stuff(){ ... }
      };
      
      template<typename T>
      struct Foo<T, typename boost::enable_if<has_const_iterator<T> >::type> {
          void do_stuff(){ ... }
      };
      

      在 C++11 中,您可以像这样使用Tick

      TICK_TRAIT(has_const_iterator)
      {
          template<class T>
          auto require(const T&) -> valid<
              has_type<typename T::const_iterator>
          >;
      };
      
      template <typename T, typename Enable = void>
      struct Foo;
      
      template <typename T>
      struct Foo< T, TICK_CLASS_REQUIRES(std::is_fundamental<T>::value)>{ 
         void do_stuff(){ ... }
      };
      
      template<typename T>
      struct Foo<T, TICK_CLASS_REQUIRES(has_const_iterator<T>())> {
          void do_stuff(){ ... }
      };
      

      还可以使用Tick 进一步增强特征以实际检测const_iterator 实际上也是一个迭代器。假设我们定义了一个简单的is_iterator 特征,如下所示:

      TICK_TRAIT(is_iterator,
          std::is_copy_constructible<_>)
      {
          template<class I>
          auto require(I&& i) -> valid<
              decltype(*i),
              decltype(++i)
          >;
      };
      

      然后我们可以定义has_const_iterator trait 来检查const_iterator 类型是否匹配is_iterator trait,如下所示:

      TICK_TRAIT(has_const_iterator)
      {
          template<class T>
          auto require(const T&) -> valid<
              has_type<typename T::const_iterator, is_iterator<_>>
          >;
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-11-26
        • 1970-01-01
        • 2010-10-28
        • 1970-01-01
        • 2013-03-26
        • 1970-01-01
        • 1970-01-01
        • 2011-04-05
        相关资源
        最近更新 更多