将指针值类型为的无序映射上的迭代器转换为常量引用值类型的同一映射上的迭代器
Transform an iterator on an unordered_map with pointer value type to an iterator on the same map with const reference value type
我有以下类:
#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
的尝试
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;
};
不幸的是,它没有编译,并给出以下错误:
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;
如果boost迭代器的使用不是强制性的,那么您可以编写自己的迭代器。我正在发布一个,它满足ForwardIterator。您可以简单地将其扩展到BidirectionalIterator(不过,它可能有点乏味)。
在发布之前,恐怕我无法满足您的要求(除了使用boost迭代器);使用std::pair<char, const Node*>
而不是std::pair<char, const Node&>
,因为后者禁止复制。也许这就是阻止您编译boost::transform_iterator
示例的原因(我不确定;我对boost迭代器不太熟悉)。
无论如何,这是code.cpp(125行)。包含main
测试功能:
#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;
};
编译命令:
g++ -std=c++11 -g -O2 -Wall code.cpp
我的输出:
3 leaf3
2 leaf2
1 leaf1
MyIterator
是作为模板类编写的,因此当您想将std::unordered_map
更改为例如std::map
时,不需要修改MyIterator
;)
使事情复杂化的是operator*
必须返回对std::pair
的引用;这意味着某个地方必须存在std::pair
的(非临时)对象,否则该引用将成为悬空引用。与operator->
相同(将"reference"替换为"pointer")。
这里,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>>
。如果选择使用std::reference_wrapper
替代方案,请将it->second->name()
替换为it->second.get().name()
。
我认为这可能是boost::indirect_iterator
存在的原因。从(琐碎的)map<char, char *>
:上的boost文档中改编一个示例
#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;
}
- 错误 C2760:语法错误:映射迭代器上意外的标记"标识符",预期的";"
- 映射迭代器与运算符不匹配
- 使用迭代器替换映射中的常量项的方法
- 映射中循环的迭代器
- std::使用迭代器映射查找距离,程序不会终止
- 创建以向量/列表迭代器为键的映射
- 我可以将映射迭代器与 OpenMP 并行使用吗?
- c++ 在循环中删除迭代器(映射的映射)
- std::map 擦除 - 将迭代器传递给错误的映射
- BAD_ACCES映射 C++ 上的迭代器
- 映射/集迭代器不可取消引用 (C++) - 调试断言失败
- 取消引用映射迭代器时返回对临时的引用
- 来自并发哈希映射的迭代器是否安全
- 为什么映射迭代器显示基本操作数无效错误
- C++有关映射和迭代器的错误
- 复制映射迭代器对值的省略
- ->第一个/第二个到空映射迭代器开始
- 将迭代器映射到特定字段上的迭代器(可以使用 Boost)
- c++如何将迭代器映射的集合作为键
- 有没有一种方法可以将迭代器映射到它的容器