【发布时间】:2013-10-31 21:24:54
【问题描述】:
为了乐趣和利润™,我正在用 C++ 编写一个 trie 类(使用 C++11 标准。)
我的trie<T> 有一个迭代器trie<T>::iterator。 (它们实际上都是功能const_iterators,因为你不能修改trie 的value_type。)迭代器的类声明部分看起来像这样:
template<typename T>
class trie<T>::iterator : public std::iterator<std::bidirectional_iterator_tag, T> {
friend class trie<T>;
struct state {
state(const trie<T>* const node, const typename std::vector<std::pair<typename T::value_type, std::unique_ptr<trie<T>>>>::const_iterator& node_map_it ) :
node{node}, node_map_it{node_map_it} {}
// This pointer is to const data:
const trie<T>* node;
typename std::vector<std::pair<typename T::value_type, std::unique_ptr<trie<T>>>>::const_iterator node_map_it;
};
public:
typedef const T value_type;
iterator() =default;
iterator(const trie<T>* node) {
parents.emplace(node, node->children.cbegin());
// ...
}
// ...
private:
std::stack<state> parents;
// ...
};
注意node 指针声明为const。这是因为(在我看来)迭代器不应该修改它指向的节点;它只是一个迭代器。
现在,在我的主要 trie<T> 类的其他地方,我有一个具有通用 STL 签名的擦除函数——它需要一个 iterator 来擦除数据(并返回一个 iterator 到下一个对象)。
template<typename T>
typename trie<T>::iterator trie<T>::erase(const_iterator it)
{
// ...
// Cannot modify a const object!
it.parents.top().node->is_leaf = false;
// ...
}
编译器报错是因为node 指针是只读的! erase 函数肯定应该修改迭代器指向的 trie,即使迭代器不应该这样做。
所以,我有两个问题:
-
iterator的构造函数应该公开吗?trie<T>有必要的begin()和end()成员,当然trie<T>::iterator和trie<T>是共同的朋友,但我不'不知道约定是什么。将它们设为私有将解决我对从迭代器的构造函数中删除const“承诺”的许多焦虑。 -
关于迭代器及其
node指针的正确const语义/约定是什么? 从来没有人向我解释过这一点,我在网上找不到任何教程或文章。这可能是更重要的问题,但它确实需要大量的计划和适当的实施。我想它可以通过实现 1 来规避,但这是事情的原理!
【问题讨论】:
-
你实现的是
const_iterator。 -
是的。 trie 将类似数组的对象拆分为树,因此“horse”和“horses”(例如)共享基本字符串“horse”。在这样的结构中,数据不是直接可变的。
-
这是
friends 无法解决的问题:给用户一个常量,给你的friend class trie一个非常量。 -
@rici:问题是,您可以使用
const_iterator来修改容器,但只能通过容器上的非 const 函数间接修改,例如 @987654347 @。因此,提问者使用指向 const 的指针来防止迭代器直接在其自己的成员函数中进行修改(毕竟这正是const的用途)。那么,问题是容器应该如何使用const_iterator来获得一个非常量指针,指向容器中它应该进行修改的位置。 -
@SteveJessop:好点。我不知何故完全错过了
erase采用const_iterator,而不是iterator的事实。谢谢。