【问题标题】:How to restrict an iterator to being a forward iterator?如何将迭代器限制为前向迭代器?
【发布时间】:2012-02-03 19:20:54
【问题描述】:

我有一个函数需要多次枚举一个迭代器,但是according to MSDN, “一旦你增加了输入迭代器的任何副本,其他副本都不能安全地进行比较、取消引用或增加。”

因此,为了让事情变得更简单,我不想为复制数据并枚举副本的非前向迭代器创建单独的实现,而是希望将我的方法限制为仅接收前向迭代器,并静态拒绝输入迭代器。

现在我有类似的东西:

template<typename It, typename TCallback /*signature: bool(value_type)*/>
bool EnumerateTwice(const It &begin, const It &end, TCallback callback)
{
    for (It it = begin; it != end; ++it)
        if (!callback(*it))
            return false;
    for (It it = begin; it != end; ++it)
        if (!callback(*it))
            return false;
    return true;
}

但没有什么限制 It 成为前向迭代器。

如何对模板化函数施加限制? (C++03)

【问题讨论】:

    标签: c++ templates iterator


    【解决方案1】:

    您可以使用 SFINAE 并将 bool 替换为:

    typename enable_if<
       is_same<typename std::iterator_traits<It>::iterator_category,
               std::forward_iterator_tag>::value,
       bool>::type
    

    如果您不想从 Boost 或 TR1 中提取 is_sameenable_if,可能需要自己定义:

    template <typename A, typename B>
    struct is_same { static const bool value = false; };
    
    template <typename T>
    struct is_same<T, T> { static const bool value = true; };
    
    template <bool, typename> struct enable_if { };
    template <typename T> struct enable_if<true, T> { typedef T type; };
    

    【讨论】:

    • +1 噢噢噢不知道迭代器被标记了!这很方便。非常感谢! :)
    • 这将拒绝双向和随机访问迭代器。使用 is_base_of 而不是 is_same 以获得正确的语义。
    • @ildjarn:确实,但是 OP 说他只想要一个前向迭代器。我相信他可以为其他条件组合布尔值,或者确实按照您的建议使用继承。
    • @Kerrek :OP 说他想静态拒绝输入迭代器,而不是将迭代器限制为仅转发迭代器(这很愚蠢)。由于这个确切原因,标准迭代器标签已经使用继承——forward_iterator_tag 继承自 input_iterator_tagbidirectional_iterator_tag 继承自 forward_iterator_tagrandom_access_iterator_tag 继承自 bidirectional_iterator_tag
    • (澄清一下,双向迭代器 is-a 前向迭代器和随机访问迭代器 is-a 双向迭代器,因此将迭代器限制为前向迭代器应该包括双向迭代器和随机访问迭代器。同时,前向迭代器is-an输入迭代器,但反过来不成立。迭代器标签继承层次已经反映,并且一直反映,这些关系。)
    【解决方案2】:

    未测试,但您可以尝试以下方式:

    template<typename It, typename TCallback /*signature: bool(value_type)*/>
    bool EnumerateTwice_Interal(const It &begin, const It &end, TCallback callback, 
          std::forward_iterator_tag)
    {
         //do your things
    }
    
    template<typename It, typename TCallback /*signature: bool(value_type)*/>
    bool EnumerateTwice(const It &begin, const It &end, TCallback callback)
    {
        EnumerateTwice_Internal(begin, end, callback,
            typename std::iterator_traits<It>::iterator_category());
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用std::enable_if

      #include <iterator>
      #include <type_traits>
      #include <utility>
      
      template <typename It, typename TCallback>
      typename std::enable_if<std::is_base_of<std::forward_iterator_tag,
                              typename std::iterator_traits<It>::iterator_category>::value,
                          bool>::type
      EnumerateTwice(It begin, It end, TCallback) {
          ...
      }
      

      这使用了 C++11 中的类,但所有这些也可以在 C++03 中完成。

      【讨论】:

        【解决方案4】:

        为了扩展罗德里戈的回答——我找到了这个解决方案,并认为值得一提:

        struct True { unsigned char _[2]; operator bool() const { return true; } };
        char is_forward_iterator(std::input_iterator_tag const *) { return 0; }
        True is_forward_iterator(std::forward_iterator_tag const *) { return True(); }
        

        现在,如果你想在某个函数中检查它,你可以说:

        if (is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0)))
        {
            ...
        }
        

        如果你想在模板中检查它,你可以检查:

        sizeof(
          is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0))
        ) > 1
        

        这种方法的主要优点是它避免了声明模板(例如,为了提高编译速度)。

        【讨论】:

          猜你喜欢
          • 2014-10-14
          • 2012-12-13
          • 2020-11-06
          • 2014-01-20
          • 2010-10-27
          • 2017-09-02
          • 1970-01-01
          • 2015-02-27
          • 1970-01-01
          相关资源
          最近更新 更多