首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将具有指针值类型的unordered_map上的迭代器转换为具有const引用值类型的同一映射上的迭代器

将具有指针值类型的unordered_map上的迭代器转换为具有const引用值类型的同一映射上的迭代器
EN

Stack Overflow用户
提问于 2016-07-24 21:59:01
回答 2查看 1.6K关注 0票数 5

我有以下课程:

代码语言:javascript
复制
#include <unordered_map>
#include <memory>


class Node {
public:
    typedef std::unique_ptr<Node> ptr_type;
    typedef std::unordered_map<char, ptr_type> map_type;

    typedef /**???**/ const_iterator;

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

private:
    map_type _children;
};

正如您所看到的,我希望这个类的用户能够迭代_children的元素,而不需要修改它们。这就是为什么我希望创建一个迭代器,它指向pair<char, const Node&>类型的元素,而不是pair<char, ptr_type>

对于手头的任务来说,创建一个基本迭代器类似乎有点过于复杂。我已经看过boost迭代器,我认为transform_iterator可能是前进的道路,但我还没有找到如何使它工作。

在我做这个的时候,有人知道我在哪里可以找到在boost-iterators中定义的不同迭代器的例子吗?对于每种类型,在文档中只有一个例子,它们并不总是适合我的需求(我是这个库的新手,我可能错过了一些显而易见的东西)。

更新:下面是我使用boost::transform_iterator的尝试

代码语言:javascript
复制
class Node {
public:
    typedef std::unique_ptr<Node> ptr_type;
    typedef std::unordered_map<char, ptr_type> map_type;


    struct Transformer {
        std::pair<char, const Node&> operator()(const std::pair<char, ptr_type> &p) const {
            return std::pair<char, const Node&>(p.first, *p.second);
        }
    };

    typedef boost::transform_iterator<Transformer, map_type::const_iterator, std::pair<char, const Node&>&, std::pair<char, const Node&>> const_iterator;

    const_iterator begin() const {
        return boost::make_transform_iterator<Transformer, map_type::const_iterator>(_children.begin(), Transformer());
    }
    const_iterator end() const {
        return boost::make_transform_iterator<Transformer, map_type::const_iterator>(_children.end(), Transformer());
    }

private:
    map_type _children;
};

不幸的是,它没有编译,并给出了以下错误:

代码语言:javascript
复制
error: no type named ‘type’ in ‘boost::mpl::eval_if<boost::is_same<boost::iterators::use_default, boost::iterators::use_default>, boost::result_of<const Node::Transformer(const std::pair<const char, std::unique_ptr<Node> >&)>, boost::mpl::identity<boost::iterators::use_default> >::f_ {aka struct boost::result_of<const Node::Transformer(const std::pair<const char, std::unique_ptr<Node> >&)>}’
     typedef typename f_::type type;
EN

回答 2

Stack Overflow用户

发布于 2016-08-19 16:58:52

如果使用boost迭代器不是强制性的,您可以编写自己的迭代器。我发布了一个,这满足了ForwardIterator。您可以将其扩展到BidirectionalIterator (不过,这可能有点乏味)。

在发布之前,我恐怕无法满足您的需求(除了使用boost-迭代器);使用std::pair<char, const Node*>而不是std::pair<char, const Node&>,因为后者禁止复制。也许这就是阻止您编译boost::transform_iterator示例的原因(我不确定;我不太熟悉boost迭代器)。

无论如何,这是code.cpp (125行长)。用于测试的main功能包括:

代码语言:javascript
复制
#include <unordered_map>
#include <memory>

class Node;

template <class Map>
class MyIterator {
public:
    // iterator member typedefs
    using iterator_category = std::forward_iterator_tag;
    using value_type = std::pair<char, const Node*>;
    using difference_type = std::ptrdiff_t;
    using pointer = value_type*;
    using reference = value_type&;

    // typedef for underlying iterator
    using underlying_iterator = typename Map::const_iterator;

    // constructors
    // takes an underlying iterator
    explicit MyIterator(underlying_iterator it) : _it(std::move(it)) {}
    // default constructor; required by ForwardIterator
    MyIterator() = default;

    // dereference; required by InputIterator
    reference operator*() {
        update();
        return _p;
    }

    // dereference; required by InputIterator
    pointer operator->() {
        update();
        return &_p;
    }

    // increment; required by Iterator
    MyIterator<Map>& operator++() {
        ++_it;
        return *this;
    }

    // increment; required by InputIterator
    MyIterator<Map> operator++(int) {
        auto mit = *this;
        ++*this;
        return mit;
    }

    // comparison; required by EqualityComparable
    bool operator==(const MyIterator<Map>& mit) const {
        return _it == mit._it;
    }

    // comparison; required by InputIterator
    bool operator!=(const MyIterator<Map>& mit) const {
        return !(*this == mit);
    }

private:
    // this method must be called at dereference-time but not
    // traverse-time in order to prevent UB at a wrong time.
    void update() {
        _p = value_type{_it->first, &*(_it->second)};
    }

    // the underlying iterator that tracks the map
    underlying_iterator _it;
    // the pair of the desired type. without it, e.g. operator-> doesn't
    // work; it has to return a pointer, and the pointed must not be a
    // temporary object.
    value_type _p;
};

class Node {
public:
    typedef std::unique_ptr<Node> ptr_type;
    typedef std::unordered_map<char, ptr_type> map_type;

    typedef MyIterator<map_type> const_iterator;

    const_iterator begin() const {
        return const_iterator{_children.begin()};
    }
    const_iterator end() const {
        return const_iterator{_children.end()};
    }

private:
    map_type _children;

// additional members for testing purposes.
public:
    Node(std::string name) : _name(std::move(name)) {}
    Node(std::string name, map_type&& children) :
        _children(std::move(children)), _name(std::move(name)) {}
    std::string const& name() const {
        return _name;
    }
private:
    std::string _name;
};

#include <iostream>

// test program; construct a simple tree and print children.
int main() {
    typedef std::unique_ptr<Node> ptr_type;
    typedef std::unordered_map<char, ptr_type> map_type;

    ptr_type leaf1(new Node("leaf1"));
    ptr_type leaf2(new Node("leaf2"));
    ptr_type leaf3(new Node("leaf3"));
    map_type branch;
    branch.emplace('1', std::move(leaf1));
    branch.emplace('2', std::move(leaf2));
    branch.emplace('3', std::move(leaf3));
    Node parent("parent", std::move(branch));

    for (auto it = parent.begin(); it != parent.end(); ++it) {
        std::cout << it->first << ' ' << it->second->name() << '\n';
    }

    return 0;
};

编译命令:

代码语言:javascript
复制
g++ -std=c++11 -g -O2 -Wall code.cpp

我的产出:

代码语言:javascript
复制
3 leaf3
2 leaf2
1 leaf1

MyIterator是作为模板类编写的,这样当您想要将std::unordered_map更改为例如std::map时,就不需要修改MyIterator ;)

更复杂的是,(non-temporary)必须返回对std::pair的引用;这意味着必须在某个地方存在std::pair的一个std::pair对象,否则该引用就会变成一个悬空引用。对于operator->也是如此(将“引用”替换为“指针”)。

在这里,MyIterator::_p是引用被接受的std::pair。这是在更新时分配的副本,std::pair<char, const Node&> (包含引用的对)禁止这一点。

std::pair<char, const Node&>的替代品有std::pair<char, const Node*>std::pair<char, std::reference_wrapper<const Node>>。如果选择使用it->second->name()替代方案,则将it->second.get().name()替换为std::reference_wrapper

票数 4
EN

Stack Overflow用户

发布于 2016-07-25 11:07:37

我认为这可能是boost::indirect_iterator存在的原因。在(琐碎的) map<char, char *>上修改boost文档中的示例

代码语言:javascript
复制
#include <iostream>
#include <map>
#include <boost/iterator/indirect_iterator.hpp>


int main() {
    char characters[] = "abcdefg";
    size_t ncharacters = sizeof characters - 1;
    char *charptr[ncharacters];

    for (size_t i = 0; i < ncharacters; ++i) {
        charptr[i] = &characters[i];
    }

    std::map <char, char *> map1;
    for (size_t i = 0; i < ncharacters; ++i) {
        map1[characters[i]] = charptr[i]; /* Trivial, just to demonstrate */
    }

    boost::indirect_iterator<char * const*, char const> const_indirect_first(charptr),
                                                        const_indirect_last(charptr + ncharacters);

    std::copy(const_indirect_first, const_indirect_last, std::ostream_iterator<char>(std::cout, " "));
    std::cout << std::endl;

    return 0;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38557243

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档