【问题标题】:How do I convert iterator to const_iterator in my custom list iterator class?如何在我的自定义列表迭代器类中将迭代器转换为 const_iterator?
【发布时间】:2020-01-03 04:38:20
【问题描述】:

我正在尝试在 C++ 中实现我自己的双向链表 (my_list) 代码,特别是我的列表的迭代器类。我的问题是我想进行从迭代器到 const_iterator 的隐式转换,例如代码 my_list::iterator it = l.begin(); 其中 lmy_list 的一个实例进行编译。但是,如果我的编译器不抱怨,我找不到这样做的方法。

下面是实现列表节点和迭代器类的代码:

template<class T> class node {
    node(const T& t = T()):data(t),next(0),prev(0) {}
    T data;
    node* next;
    node* prev;

    friend class my_list<T>;
    friend class my_list_iterator<T>;
};

template<class T> class my_list_iterator {
    public:
            // increment and decrement operators
            my_list_iterator operator++();
            my_list_iterator operator++(int);
            my_list_iterator operator--();
            my_list_iterator operator--(int);

            // bool comparison iterators
            bool operator==(const my_list_iterator& other) const {return pos_==other.pos_;}
            bool operator!=(const my_list_iterator& other) const {return pos_!=other.pos_;}

            // member access
            T& operator*() const {return pos_->data;}
            T* operator->() const {return &(pos_->data);}

            // implicit conversion to const iterator
            operator my_list_iterator<const T>() {return my_list_iterator<const T>(pos_);}
    private:
            node<T>* pos_;
            explicit my_list_iterator(node<T>* p=0):pos_(p) {}
            friend class my_list<T>;
};

我省略了 my_list 实现,但如果您认为它相关,我可以包含它。当我测试这段代码时,它不会在 GCC 上编译并出现以下错误:

In file included from test.cpp:2:
my_list.h: In instantiation of ‘my_list_iterator<T>::operator my_list_iterator<const T>() [with T = int]’:
test.cpp:12:49:   required from here
my_list.h:37:48: error: no matching function for call to ‘my_list_iterator<const int>::my_list_iterator(node<int>*&)’
   operator my_list_iterator<const T>() {return my_list_iterator<const T>(pos_);}
                                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
my_list.h:40:12: note: candidate: ‘my_list_iterator<T>::my_list_iterator(node<T>*) [with T = const int]’
   explicit my_list_iterator(node<T>* p=0):pos_(p) {}
            ^~~~~~~~~~~~~~~~
my_list.h:40:12: note:   no known conversion for argument 1 from ‘node<int>*’ to ‘node<const int>*’
my_list.h:20:25: note: candidate: ‘constexpr my_list_iterator<const int>::my_list_iterator(const my_list_iterator<const int>&)’
 template<class T> class my_list_iterator {
                         ^~~~~~~~~~~~~~~~
my_list.h:20:25: note:   no known conversion for argument 1 from ‘node<int>*’ to ‘const my_list_iterator<const int>&’
my_list.h:20:25: note: candidate: ‘constexpr my_list_iterator<const int>::my_list_iterator(my_list_iterator<const int>&&)’
my_list.h:20:25: note:   no known conversion for argument 1 from ‘node<int>*’ to ‘my_list_iterator<const int>&&’

有人可以帮我解决我做错了什么吗?

【问题讨论】:

  • 确认一下,你想要一个不能修改它所代表的对象的迭代器,是吗?
  • 很好,迈尔斯。没注意到。
  • 这通常使用不同类型的迭代器来完成,而不是使用不同包含类型的相同迭代器。在标准库中,这是使用 iteratorconst_iterator 执行的,两者都围绕相同的类型进行模板化。
  • @user4581301 是的。我想要一个只读访问迭代器,与 std::list::const_iterator 的行为方式完全相同。至于您的最后一条评论,我真的需要执行两次代码吗?我基本上是在尝试创建从 iterator 到 iterator 的隐式转换,这不可能吗?

标签: c++ iterator doubly-linked-list const-iterator


【解决方案1】:
my_list.h:40:12: note: candidate: ‘my_list_iterator<T>::my_list_iterator(node<T>*) [with T = const int]’
   explicit my_list_iterator(node<T>* p=0):pos_(p) {}
            ^~~~~~~~~~~~~~~~
my_list.h:40:12: note:   no known conversion for argument 1 from ‘node<int>*’ to ‘node<const int>*’

node&lt;int&gt;node&lt;const int&gt; 是不相关的类型。您不能将指向 node&lt;int&gt; 的指针传递给需要指向 node&lt;const int&gt; 的指针的函数。

您可以将const 上移一级并将迭代器模板化到节点类型上,而不是在包含的类型上模板化您的迭代器类:

template<class Node> class my_list_iterator {
public:

    //...

    // member access
    auto& operator*() const {return pos_->data;}
    auto* operator->() const {return &(pos_->data);}

    // implicit conversion to const iterator
    operator my_list_iterator<const Node>() {return my_list_iterator<const Node>{pos_};}
private:
    Node* pos_;
    explicit my_list_iterator(Node* p=0):pos_(p) {}
    friend class my_list<type>;
};

template <class T> class my_list {
public:
    using iterator = my_list_iterator<node<T>>;
    using const_iterator = my_list_iterator<const node<T>>;
    //...
};

Live Demo

现在您将指向 node&lt;int&gt; 的指针传递给期望指向 const node&lt;int&gt; 的指针的函数,这很好。

【讨论】:

  • 我明白了。但是我不能在节点类中定义一个隐式转换器吗?类似于:operator node<const t>() {return node<const t>(*this);}</const></const>
  • 定义从node&lt;T&gt;node&lt;const T&gt; 的转换需要复制node。您不想复制迭代器来复制它所引用的数据。
  • 也许我遗漏了一些东西,但是如果我们按照你的例子,我们如何在常量迭代器上使用前缀增量?既然这需要改变节点本身,不是吗?
  • 不,递增迭代器只涉及更改迭代器的指针以指向下一个节点。节点本身没有改变。
【解决方案2】:

这是一种方法:

template <typename T>
class my_list {
public:
    using iterator = my_list_iterator<T>;
    using const_iterator = my_list_iterator<const T>;

    const_iterator cbegin() const { return {/*...*/}; }
    const_iterator cend() const { return {/*...*/}; }
    const_iterator begin() const { return {/*...*/}; }
    const_iterator end() const { return {/*...*/}; }
    iterator begin() { return {/*...*/}; }
    iterator end() { return {/*...*/}; }
};

【讨论】:

  • 我不确定我是否遵循。我希望能够编写 my_list<t>::const_iterator it = l.begin()</t> 而不必每次执行此类操作时都使用不同的开始函数。在STL实现中我可以做到这一点没有问题,所以必须有一种方法来实现这个......
  • 大量重载的目的是为了让用户不用太费心。例如,如果lconstauto it = l.begin(); 将使用正确的重载。基于范围的 for 循环也会。如果您作为用户想要确保使用begin()const_iterator 版本,我们cbegin(),就像在标准库中一样。 3 个begin() 版本的示例:std::vector::begin
猜你喜欢
  • 2013-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
  • 2016-04-05
  • 2014-02-21
  • 2021-03-19
  • 1970-01-01
相关资源
最近更新 更多