简体   繁体   English

如何习惯性地编写const迭代器?

[英]How to write a const iterator idiomatically?

I am writing a linked list implementation, but I've gotten stuck on how to implement a const iterator accessed through cbegin() alongside the normal iterator. 我正在编写一个链表实现,但是我仍然对如何实现通过cbegin()与普通迭代器一起访问的const迭代器cbegin() Here's my attempt at a solution: 这是我尝试的解决方案:

#include <iostream>

using namespace std;

template <typename T>
class ListNode {
public:
    ListNode<T>* next_;
    ListNode<T>* prev_;
    T data_;

    ListNode():
        next_(nullptr),
        prev_(nullptr)
    {}
};

template <typename T>
class ListIterator
{
    typedef ListNode<T> node;
    typedef ListNode<T>* pointer;
    pointer p_;

public:
    ListIterator(pointer p) : p_(p) {}
    T& operator*() { return p_->data_; }
};

template<typename T>
class List
{
public:
    typedef ListNode<T> node;
    typedef ListNode<T>* pointer;
    typedef ListIterator<T> iterator;
    typedef ListIterator<const T> constIterator;

    List() :
        head_(nullptr),
        tail_(nullptr),
        size_(0)
    {}

    void pushBack(pointer p) {
        p->next_ = nullptr;
        p->prev_ = nullptr;

        if (size_ == 0) {
            head_ = p;
        } else {
            tail_->next_ = p;
            p->prev_ = tail_;
        }

        tail_ = p;
        ++size_;
    }

    iterator begin() { return head_; }
    iterator end() { return nullptr; }
    constIterator cbegin() { return head_; }
    constIterator cend() { return nullptr; }

private:
    pointer head_;
    pointer tail_;
    unsigned int size_;
};

class Dog {
public:
    int age;
};

int main() {
    auto list = List<Dog>();
    auto dogNode = ListNode<Dog>();
    list.pushBack(&dogNode);
    auto b = list.cbegin();
}

When I try to run this, I get the error error: no viable conversion from returned value of type 'List<Dog>::pointer' (aka 'ListNode<Dog> *') to function return type 'List<Dog>::constIterator' (aka 'ListIterator<const Dog>') 当我尝试运行此命令时,出现错误error: no viable conversion from returned value of type 'List<Dog>::pointer' (aka 'ListNode<Dog> *') to function return type 'List<Dog>::constIterator' (aka 'ListIterator<const Dog>')

This error makes sense to me, but I can't figure out a good workaround that doesn't involve writing a separate ConstListIterator class (this works, but it feels wrong to copy-pasta all the operator overloading code from ListIterator --most of it omitted from this sample). 这个错误对我来说很有意义,但我不能想出一个好的工作环境,不涉及编写单独的ConstListIterator类(这个作品,但感觉不对的复制面食的所有运营商超载代码ListIterator --most的(此示例中省略了)。

In this question , the author uses the approach of parametrizing ListIterator with TNode , which in this context would be ListNode<Dog> rather than Dog . 这个问题 ,作者使用参数化的方法ListIteratorTNode ,这在该上下文中是ListNode<Dog>而不是Dog The problem with this approach is that then the operator* can't return an actual Dog , because the ListIterator class doesn't know about the typename Dog . 这种方法的问题在于, operator*无法返回实际的Dog ,因为ListIterator类不知道类型名称Dog So that solution just hardcodes in the type of data_ , so you lose all the benefits of the metaprogramming. 因此,该解决方案只是将数据编码为data_类型,因此您将失去元编程的所有好处。

Is there a trick to make this work nicely, or should I just have a separate ConstListIterator class? 有没有技巧可以使此工作顺利进行,还是我应该只拥有一个单独的ConstListIterator类?

Thank you! 谢谢!

The reason for your error is that you are trying to return head , which is of type ListNode<T>* from a function with the return type constIterator AKA ListIterator<const T> . 错误的原因是您试图从返回类型为constIterator AKA constIterator ListIterator<const T>的函数中返回head ,它的类型为ListNode<T>*

To answer your question, the only difference between an iterator and a const_iterator is that a const_iterator returns a const reference to the value_type of the container instead of just a reference. 要回答您的问题, iteratorconst_iterator之间的唯一区别是const_iterator返回对容器的value_type的const引用,而不仅仅是引用。 Therefore, passing const T as the template argument should result in the desired functionality. 因此,将const T作为模板参数传递应该会产生所需的功能。 Since operator* returns T& , if T is instead const T , then it returns const T& , which is correct. 由于operator*返回T& ,如果T代替const T ,则它返回const T& ,这是正确的。 It seems that the issue you were having was because you were returning the wrong types from your iterator functions, as noted above. 如上所述,看来您遇到的问题是因为您从iterator函数返回了错误的类型。

Your iterator functions should instead look like this: 您的迭代器函数应该看起来像这样:

iterator begin() { return iterator{head_}; }
iterator end() { return iterator{nullptr}; }
constIterator cbegin() { return constIterator{head_}; }
constIterator cend() { return constIterator{nullptr}; }

This returns iterator and constIterator objects that are constructed using the pointers head_ and nullptr , which is what you want. 这将返回使用指针head_nullptr构造的iteratorconstIterator对象,这是您想要的。

The template parameter you used for the iterators, the value_type of the list, is correct. 您用于迭代器的模板参数(列表的value_type是正确的。

Having the node type be a template argument for the iterator is reasonable and allows 将节点类型作为迭代器的模板参数是合理的,并且允许

using constIterator=ListIterator<const ListNode<T>>;

which conveniently precludes structural modifications as well as content modifications. 方便地排除结构修改和内容修改。 It allows cbegin to work as written (implicitly adding const to head_ 's type). 它允许cbegin以书面cbegin工作(隐式将const添加到head_的类型中)。 It also allows T to be const itself (which works for a linked list: it never needs to relocate anything ). 它还允许T本身为const (它适用于链表:它永远不需要重定位 )。

To define operator* , you can just use auto : 要定义operator* ,您可以只使用auto

auto& operator*() const {return p_->data_;}

The constness of *p_ carries over to data_ and thence to the return type. *p_继承到data_ ,然后返回到返回类型。

Before C++14 在C ++ 14之前

In C++11, you can equip ListNode with 在C ++ 11中,您可以为ListNode配备

using value_type=T;

and use type traits like is_const and conditional to derive const T from const ListNode<T> . 并使用is_const类的类型特征和conditionalconst ListNode<T>导出const T

In C++03, you can directly write a SFINAE-based trait for the purpose. 为此,在C ++ 03中,您可以直接编写基于SFINAE的特征。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM