【问题标题】:How to call const_iterator for non const class? [duplicate]如何为非 const 类调用 const_iterator? [复制]
【发布时间】:2014-01-27 20:27:23
【问题描述】:

我阅读了一些与此问题相关的其他主题,但没有为我的问题提供解决方案。希望大家能给我一些意见或建议。

我正在尝试实现这个名为Map 的类。它应该包含 2 个迭代器 - iteratorconst_iterator

我已经实现了它们 - iterator 继承自 const_iterator,并且在 Map 类中我具有以下功能:

iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;

我们获得了一个示例文件,以了解实现所需的内容。 里面有如下代码:

Map<std::string,int> msi;

...

// print map
for(Map<std::string,int>::const_iterator it = msi.begin(); it != msi.end(); ++it) {
// more stuff here
}

由于 msi 是一个非常量 Map 实例,msi.begin() 调用 iterator begin() 而不是 const_iterator begin() const,从而导致意外行为。

假设示例文件没问题,我该如何让msi.begin() 调用正确的const_iterator 函数? (考虑到它,迭代器的类型是const_iterator)。

编辑:关于自动转换的讨论,我决定添加我的迭代器类,请指出我的错误。

class Map {

    //...

    public:

        class const_iterator {

        private:

            Node* currNode;

        public:


            const_iterator(Node* cur_node = NULL) : currNode(cur_node) {}

            const_iterator& operator++() {
                currNode = currNode->next;
                return *this;
            }

            const_iterator operator++(int) {
                const_iterator old = *this;
                ++(*this);
                return old;
            }

            bool operator!=(const_iterator const& curr) {

                return !(*this == curr);
            }

            string operator*() {
                // this might cause memory leak
                string toString(this->currNode->key);
                std::stringstream s;
                int tmp = this->currNode->value;
                s << tmp;
                string secondString(s.str());
                toString = toString + ":" + secondString;
                return toString;
            }

            bool operator==(const_iterator const& curr) {
                return this->currNode == curr.currNode;
            }


            void operator=(const_iterator target) {
                this = target;
            }

            //void operator=(Node* target) {
            //    this->currNode = target;
            //}
        };

        class iterator : public const_iterator {

        private:

            Node* currNode;

        public:

            iterator(Node* cur_node = NULL) : currNode(cur_node) {}

            iterator& operator++() {
                currNode = currNode->next;
                return *this;
            }

            iterator operator++(int) {
                iterator old = *this;
                ++(*this);
                return old;
            }

            bool operator==(iterator const& curr) {
                return *this == curr;
            }

            bool operator!=(iterator const& curr) {

                return !(*this == curr);
            }

            string operator*() {
                // this might cause memory leak
                string toString(this->currNode->key);
                std::stringstream s;
                int tmp = this->currNode->value;
                s << tmp;
                string secondString(s.str());
                toString = toString + ":" + secondString;
                return toString;
            }

            void operator=(iterator target) {
                this = target;
            }

        };

        //..
}

【问题讨论】:

  • 为什么调用 iterator 版本不好?

标签: c++ templates iterator constants


【解决方案1】:

如果你要定义 msi

const Map<std::string,int> msi;

而不是

Map<std::string,int> msi;

将调用 begin() 和 end() 的 const 版本

【讨论】:

    【解决方案2】:

    C++11 标准容器为此添加了cbegincend。缺少这一点,您显然总是可以将您的对象显式地转换为 const&amp; 以获得对象的 const 视图。

    不过,更根本的是,您的 iterator 没有理由不支持自动转换为 const_iterator。像这样,您根本不需要更改客户端代码。事实上,如果如您所说,iterator 继承自 const_iterator,您的代码应该已经支持这一点。

    但是,您发布的代码包含几个错误。首先,operator= 是错误的,你应该收到一个错误。更正的版本是:

    void operator=(const_iterator target) {
        currNode = target.currNode;
    }
    

    更重要的是,您的继承毫无意义。确实,您确实const_iterator 继承了iterator,但您的代码假装这从未发生过——iterator 完全重新实现了它的父类,并且与它没有任何关系。

    iterator 应该看起来像这样:

    class iterator : public const_iterator {
    public:
        iterator(Node* cur_node = NULL) : const_iterator(cur_node) {}
    };
    

    这当然要求currNodeconst_iterator 中声明为protected。该类也完全没用(但目前你的也是如此),因为它没有向const_iterator 类添加任何功能。您需要实现一个operator*,它允许修改其值。您当前的代码基本上不允许这样做,因为它返回一个新创建的字符串,而不是(类似于)对映射值的引用。

    此外,尚不清楚const_iterator 类是如何获得非const Node 指针的。这是不可能的:毕竟,它从 const Map 获取指针。

    【讨论】:

    • template&lt;class T&gt; T const&amp; as_const(T&amp; v){ return v; } // wheee
    • 您能否详细说明如何实现自动转换?我认为如果 OP 知道该怎么做,他们会的。
    • @deworde 根据 OP,转换已经实现(“iterator继承自const_iterator”)。
    • 是的,我就是这么想的,但在那种情况下,这不应该是自动工作,而不是“导致意外行为”吗?
    • 这正是我问的原因 - 但我没有得到自动转换。我可以添加我的迭代器类,也许出了点问题
    猜你喜欢
    • 2018-11-15
    • 1970-01-01
    • 2020-12-21
    • 1970-01-01
    • 2017-11-29
    • 2014-07-20
    • 2020-07-15
    • 2012-01-09
    • 1970-01-01
    相关资源
    最近更新 更多