【问题标题】:Trouble implementing const_iterator in c++在 C++ 中实现 const_iterator 时遇到问题
【发布时间】:2021-12-26 23:42:36
【问题描述】:

我正在努力为我的容器类实现一个 const_iterator,我用一个基本数组做了一个简短的可复制示例来显示问题(我知道 std::iterator 已弃用,但这是针对 C++ 中的一个项目98标准):

#include <iostream>
#include <cstdlib>
#include <iterator>

template <
    typename T
> struct remove_const { typedef T type; };
template <
    typename T
> struct remove_const < const T > { typedef T type; };

template <
    typename T
> struct add_const { typedef const T type; };

template <
    class T,
    std::size_t size
> class Array {
    public:
        template <
            class U
        > class base_iterator : public std::iterator <
            std::random_access_iterator_tag,
            U
        > {
            public:
                typedef std::iterator <
                    std::random_access_iterator_tag,
                    U
                >   iterator_type;

                base_iterator() : ptr(NULL) { }
                base_iterator(U *p) : ptr(p) { }
                base_iterator(const base_iterator &other) : ptr(other.ptr) { }
                base_iterator(const base_iterator< remove_const< U > > &other) : ptr(other.ptr) { }
                base_iterator(const base_iterator< add_const< U > > &other) : ptr(other.ptr) { }

                base_iterator   &operator =(const base_iterator &other) {
                    ptr = other.ptr;
                    return *this;
                }
                base_iterator   &operator =(const base_iterator< remove_const< U > > &other) {
                    ptr = other.ptr;
                    return *this;
                }
                base_iterator   &operator =(const base_iterator< add_const< U > > &other) {
                    ptr = other.ptr;
                    return *this;
                }
                U               &operator *(void) { return *ptr; }
                U               &operator *(void) const { return *ptr; }
                U               *operator ->(void) { return ptr; }
                U               *operator ->(void) const { return *ptr; }
                base_iterator   &operator ++(void) {
                    ++ptr;
                    return *this;
                }
                base_iterator   operator ++(int) {
                    return base_iterator(ptr++);
                }
                base_iterator   &operator --(void) {
                    --ptr;
                    return *this;
                }
                base_iterator   operator --(int) {
                    return base_iterator(ptr++);
                }
                base_iterator   operator +(const typename iterator_type::difference_type &amount) const {
                    return base_iterator(ptr + amount);
                }
                base_iterator   operator -(const typename iterator_type::difference_type &amount) const {
                    return base_iterator(ptr - amount);
                }
                base_iterator   &operator +=(const typename iterator_type::difference_type &amount) {
                    ptr += amount;
                    return *this;
                }
                base_iterator   &operator -=(const typename iterator_type::difference_type &amount) {
                    ptr -= amount;
                    return *this;
                }
                bool            operator ==(const base_iterator &other) const {
                    return ptr == other.ptr;
                }
                bool            operator !=(const base_iterator &other) const {
                    return ptr != other.ptr;
                }
                bool            operator <(const base_iterator &other) const {
                    return ptr < other.ptr;
                }
                bool            operator <=(const base_iterator &other) const {
                    return ptr <= other.ptr;
                }
                bool            operator >(const base_iterator &other) const {
                    return ptr > other.ptr;
                }
                bool            operator >=(const base_iterator &other) const {
                    return ptr >= other.ptr;
                }
            private:
                U   *ptr;
        };
        typedef base_iterator< T >                      iterator;
        typedef base_iterator< const T >                const_iterator;
        typedef std::reverse_iterator< iterator >       reverse_iterator;
        typedef std::reverse_iterator< const_iterator > const_reverse_iterator;

        iterator                begin(void) { return iterator(data); }
        const_iterator          begin(void) const { return const_iterator(data); }
        iterator                end(void) { return iterator(data + size); }
        const_iterator          end(void) const { return const_iterator(data + size); }
        reverse_iterator        rbegin(void) { return reverse_iterator(data + size - 1); }
        const_reverse_iterator  rbegin(void) const { return const_reverse_iterator(data + size - 1); }
        reverse_iterator        rend(void) { return reverse_iterator(data - 1); }
        const_reverse_iterator  rend(void) const { return const_reverse_iterator(data - 1); }

    private:
        T   data[size];
};

int main(void) {
    Array< int, 20 >    a;

    for (Array< int, 20 >::iterator it = a.begin(); it != a.end(); ++it)
        *it = 42;
    for (Array< int, 20 >::const_iterator it = a.begin(); it != a.end(); ++it)
        std::cout << *it << std::endl;
}

编译器输出

problem.cpp:106:40: error: no viable conversion from 'base_iterator<int>' to 'base_iterator<const int>'
        for (Array< int, 20 >::const_iterator it = a.begin(); it != a.end(); ++it)
                                              ^    ~~~~~~~~~
problem.cpp:24:5: note: candidate constructor not viable: no known conversion from 'Array<int, 20>::iterator' (aka 'base_iterator<int>') to 'const int *' for 1st argument
                                base_iterator(U *p) : ptr(p) { }
                                ^
problem.cpp:25:5: note: candidate constructor not viable: no known conversion from 'Array<int, 20>::iterator' (aka 'base_iterator<int>') to 'const Array<int, 20>::base_iterator<const int> &' for 1st argument
                                base_iterator(const base_iterator &other) : ptr(other.ptr) { }
                                ^
1 error generated.

所以看来问题在于base_iterator&lt; T &gt; 不能转换为base_iterator&lt; const T &gt;,但首先我想知道为什么我们需要转换?由于beginendoperator */-&gt; 有const 重载,它不应该调用const 版本吗? 其次,如果我们需要转换,我不知道该怎么做

编辑 1:我实现了 remove_constadd_const 来进行转换,但它仍然无法编译

【问题讨论】:

  • 这与您的问题无关,但您可能会遇到未来的错误:rbegin 应该只是reverse_iterator(end())(或reverse_iterator(iterator(data + size))),因为std::reverse_iterator 在您取消引用时会为您减一。 rendreverses_iterator(begin()) 相同。
  • add_const&lt;U&gt; 应该是 typename add_const&lt;U&gt;::typeremove_const&lt;U&gt; 也是如此。 (但是,当前代码将复制复制构造函数和赋值运算符。)

标签: c++ iterator c++98


【解决方案1】:

是否调用成员函数的const限定版本,仅取决于调用成员函数的表达式的类型是否为const限定。

在您的示例中,a.begin() 中的 a 被声明为 Array&lt; int, 20 &gt;,而不是 const。因此将调用begin() 的非const 限定版本。

如果您想让您的容器类符合 C++ 标准库中的 Container 要求,则必须使您的 iterator 可转换为 const_iterator。这是规定的要求之一,将使您的测试代码按预期工作。

有关容器要求的完整列表,请参阅cppreference

【讨论】:

  • 您好,感谢您的回答,我实现了add_constremove_const进行转换,但它不起作用,我们应该如何进行转换?
  • @Fayeure 你指的是std::add_conststd::remove_const吗?我不知道他们会如何在这里提供帮助。您可以通过多种方式进行转换:将iteratorconst_iterator 设为两个类。要么让两者都从base_iterator 继承并让const_iterator 采用转换构造函数,要么给iterator 一个用户定义的转换运算符。另请参阅其他答案以了解替代方法。
  • @Fayeure 另外顺便说一句,对于您这种只使用简单数组作为线性存储的容器,指针作为迭代器就足够了。除了typedef T* iterator; typedef const T* const_iterator; 等等,什么都不需要。
  • 我知道,这只是一个简单的繁殖示例,我的真实容器是一棵红黑树,项目是实现std::mapstd::vector,所以我需要实现一个迭代器类map,当然我只是为我的vector 使用常规指针
【解决方案2】:

您的编译器告诉您它无法将您的 iterator 转换为您的 const_iterator

a.begin();

由于a 是一个可变对象,因此会调用返回iteratorbegin() 重载,但会尝试将结果存储到const_iterator 中,而您设计迭代器类的方式确实如此不允许发生这种转换。

这与您不能将std::vector&lt;int&gt; 等分配给std::vector&lt;const int&gt; 的原因相同。这也是同样的根本原因。 std::vector&lt;int&gt;std::vector&lt;const int&gt; 是两个完全不同的类,它们以任何形式或方式都没有相互关联。您的两个迭代器类在同一条船上。

您需要做的是重新设计您的迭代器类,以便这种转换变得微不足道。

处理这个问题的经典方法是让iterator 继承自const iterator,类似于:

class const_iterator {
protected:

    T *p;  // The underlying pointer, or whatever is being used to reference the iterator value and/or its container.

public:

    // Implement only the `const` methods here
};

class iterator : public const_iterator {

public:

    // Implement only the non-const methods here
};

现在,神奇的转换会在需要的地方自动发生。 iteratorconst_iterator 都可以完全访问底层容器及其值。 const_iterator 只需要注意保持const 的正确性,即其operator* 重载返回const T &amp;,而iterator::operator*() 返回T &amp;,依此类推。

【讨论】:

  • 谢谢,我会这样做的,看起来更干净了,我不知道为什么我在搜索实现时才找到我的结构
  • 每一本专门研究 C++ 库算法和容器的 C++ 教科书都应该解释这种设计模式。这就是为什么你不能从随机的 Google 搜索或 Youtube 视频中真正学习 C++,而只能从有声望的教科书中学习。
猜你喜欢
  • 2022-01-18
  • 2016-03-12
  • 1970-01-01
  • 1970-01-01
  • 2018-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多