【问题标题】:How to accept generic iterators?如何接受泛型迭代器?
【发布时间】:2013-09-11 09:42:42
【问题描述】:

我希望构造函数接受任何具有 x 特征和引用类 y 的迭代器。

Class(std::iterator<std::random_access_iterator_tag, MyClass*> it);

但是当我尝试传递这样的迭代器时,编译失败并显示Candidate constructor not viable: no known conversion from 'iterator' (aka '__deque_iterator&lt;value_type, pointer, reference, __map_pointer, difference_type, __block_size&gt;') to 'std::iterator&lt;std::random_access_iterator_tag, MyClass *&gt;'

插入代码:

std::deque<MyClass*> collection_with_random_access_iterator{};
Class tmp(collection_with_random_access_iterator.begin());

我可以在这里做什么?

【问题讨论】:

  • 值得注意的是,您收到错误是因为(至少在您的实现中)deque&lt;T&gt;::iterator 不继承自 iterator&lt;random_access_iterator_tag, T&gt;(标准不要求它这样做)。 (顺便说一句,MyClass** 也是MyClass* 的一种“随机访问迭代器”(例如vector&lt;MyClass*&gt;::iterator 就是这样),但不能从任何东西派生原始指针。)
  • 虽然我们来了.. std::iterator 不是多态的,它只是一个帮助器基类,用于简化迭代器定义。它不应该那样使用。
  • @sbabbi 是的,感谢您指出这一点。从便利结构 std::iterator 继承,即使是公开的,也只是一个实现细节。
  • @gx_ 遗憾的是,c++ 标准不要求标准集合的 iterators 来自一个共同的父集合。
  • @AdamS 指针有资格作为随机访问迭代器,vector&lt;T&gt;::iteratorbasic_string&lt;T&gt;::iterator 可以是 T *。无论如何,继承无关紧要。您不想使用虚函数来取消引用迭代器。

标签: c++ c++11 polymorphism


【解决方案1】:

怎么样:

template<class Iterator>
Class(Iterator it,
  typename std::enable_if<
          std::is_same<
                  typename std::iterator_traits<Iterator>::value_type,
                  MyClass*
          >::value //checks for value_type
          &&
          std::is_base_of<
                  std::random_access_iterator_tag,
                  typename std::iterator_traits<Iterator>::iterator_category
          >::value //checks for iterator category
    >::type * = 0);

编辑你也应该考虑用std::is_convertible替换第一个std::is_same,如果你不打算修改输入,检查const MyClass*

【讨论】:

  • 谢谢,这看起来像是一个选项,但也相当大。我更喜欢更紧凑的解决方案,但我会记住这一点,如果没有,我会回来。
  • @AdamS:替代方案是接受任何类型,可能调用模板参数类似RandomIterator 来记录其非正式要求。这使代码更简单,但如果类型不符合要求,您将收到更糟糕的错误消息。或者,等待“概念”被添加到语言中,希望在明年。
  • @Mike Seymour 这实际上可以与 static_assert 结合使用(假设这是类中唯一的模板构造函数)。
【解决方案2】:

迭代器通常按值接受,然后通过委托给其他函数进行区分。

例如,

    template< typename iterator >
    Class( iterator it ) {
        init_class( * it, typename std::iterator_traits< iterator >::category() );
    }

    template< typename iterator >
    void init_class( iterator it, std::random_access_iterator_tag ) {
        for ( int i = 0; i != 42; i +=3 ) {
            do_something( it[ i ] );
        }
    }

    void do_something( MyClass * ) { … }
};

传递错误的迭代器会导致函数内部出现错误,这对用户来说可能很神秘。但这就是标准库实现中通常发生的情况,也是在发明 SFINAE 之前最初使用迭代器的方式。如果有用户常见的错误,可以具体的陷进去,引导用户到具体的错误/cmets。

如果不需要选择不同的行为,但想确保用户通过MyClass * 传递随机访问迭代器,请使用一对static_asserts 和std::is_same 条件作为sbabbi 的答案。由此产生的用户体验比纯 SFINAE 更好,因为错误消息显示“请传递随机访问迭代器”,而不是“未发现重载”。

【讨论】:

  • 值得注意的是,标签调度和/或 static_assert 不能总是替代 SFINAE,尤其是有时对于构造函数和与 is_constructible 等特征的交互。见flamingdangerzone.com/cxx11/2013/02/11/…
  • @gx_ 适合这项工作的工具。这个答案有问题还是您今天没有投票? ;v)
  • 没什么错,只是没想到要投票。 “已修复” :) (不是说您需要更多代表,但voting is important 确实如此)
猜你喜欢
  • 2011-06-30
  • 2010-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-09
  • 1970-01-01
  • 2012-02-27
  • 1970-01-01
相关资源
最近更新 更多