如何避免实现 const 和非 const 迭代器的代码重复?
我正在实现一个具有类似 STL 接口的自定义容器.我必须提供一个常规迭代器和一个 const 迭代器.两个版本的迭代器的大部分代码是相同的.如何避免这种重复?
I'm implementing a custom container with an STL-like interface. I have to provide a regular iterator and a const iterator. Most of the code for the two versions of the iterators is identical . How can I avoid this duplication?
例如,我的容器类是Foo
,我正在实现FooIterator
和FooConstIterator
.两个迭代器都必须提供像 operator++()
这样相同的方法.
For example, my container class is Foo
, and I'm implementating FooIterator
and FooConstIterator
. Both of the iterators have to provide methods like operator++()
which are identical.
我的问题类似于 如何删除相似的 const 和非 const 成员函数之间的代码重复?,但该问题的答案是特定于 const 和非 const 方法,尤其是访问器.我看不出这会如何推广到迭代器问题.
My question is similar to How do I remove code duplication between similar const and non-const member functions?, but the answer to that one is specific to const and non-const methods, especially accessors. I don't see how that might generalize to the iterator problem.
我是否应该让 FooIterator
从 FooConstIterator
派生并使用其他非常量方法对其进行扩展?这要么导致虚拟方法,要么导致方法隐藏,在这里似乎不合适.
Should I have FooIterator
derive from FooConstIterator
and extend it with additional non-const methods? That either leads to virtual methods or method hiding, which seem inappropriate here.
也许 FooIterator
应该包含一个 FooConstIterator
.虽然这种方法确实减少了实现重复,但它似乎重新引入了许多样板方法定义.
Perhaps FooIterator
should contain a FooConstIterator
. Although that approach does reduce implementation duplication, it seems to re-introduce a lot of boilerplate method definitions.
是否有巧妙的模板技术从单个定义生成两个迭代器?或者也许有一种方法――颤抖――使用预处理器来消除这些几乎相同的类.
Is there clever template technique for generating the two iterators from a single definition? Or perhaps there's a way to--shudder--use the preprocessor to stamp out these nearly identical classes.
我尝试查看我的本地 STL 实现,看看它是如何处理这个问题的.有太多帮助类,我无法理解设计,但看起来功能只是重复了.
I've tried looking at my local STL implementation to see how it handle this. There are so many helper classes that I'm having trouble grokking the design, but it looks like the functionality is simply duplicated.
在以前的项目中,我的自定义容器构建在标准 STL 容器之上,因此我不必提供自己的迭代器.在这种情况下,这不是一个选项.
In previous projects, my custom container was built on top of a standard STL container, so I didn't have to provide my own iterators. That's not an option in this case.
推荐答案
[不幸的是,最好的答案被版主删除了,因为它是一个仅链接的答案.我理解为什么不鼓励仅链接的答案;然而,删除它已经剥夺了未来寻求者非常有用的信息.该链接已保持稳定超过七年,并在撰写本文时继续有效.]
我强烈推荐由 Matt Austern 撰写的 Dobb 博士期刊原创文章 The Standard Librarian: Defining Iterators and Const Iterators",2001 年 1 月.如果这个链接坏了,现在 Dobb 博士已经停止运营,它也可以使用 这里.
I strongly recommend the original Dr. Dobb's Journal article by Matt Austern entitled "The Standard Librarian: Defining Iterators and Const Iterators", January 2001. Should that link go bad, now that Dr. Dobb's has ceased operating, it's also available here.
为了防止这个替换答案被删除,我总结一下解决方法.
To prevent this replacement answer from being deleted, I will summarize the solution.
这个想法是将迭代器作为一个模板实现一次,该模板接受一个额外的模板参数,一个布尔值,表示这是否是 const 版本.在实现中 const 和非 const 版本不同的任何地方,您都可以使用模板机制来选择正确的代码.Matt Austern 的机制被称为 choose
.它看起来像这样:
The idea is to implement the iterator once as a template that takes an extra template parameter, a boolean that says whether or not this is the const version. Anywhere in the implementation where the const and non-const versions differ, you use a template mechanism to select the correct code. Matt Austern's mechanism was called choose
. It looked like this:
template <bool flag, class IsTrue, class IsFalse>
struct choose;
template <class IsTrue, class IsFalse>
struct choose<true, IsTrue, IsFalse> {
typedef IsTrue type;
};
template <class IsTrue, class IsFalse>
struct choose<false, IsTrue, IsFalse> {
typedef IsFalse type;
};
如果您对 const 和非 const 迭代器有单独的实现,那么 const 实现将包括如下类型定义:
If you had separate implementations for const and non-const iterators, then the const implementation would include typedefs like this:
typedef const T &reference;
typedef const T *pointer;
并且非常量实现将具有:
and the non-const implementation would have:
typedef T &reference;
typedef T *pointer;
但是使用 choose
,您可以拥有一个基于额外模板参数进行选择的实现:
But with choose
, you can have a single implementation that selects based on the extra template parameter:
typedef typename choose<is_const, const T &, T &>::type reference;
typedef typename choose<is_const, const T *, T *>::type pointer;
通过使用底层类型的 typedef,所有迭代器方法都可以具有相同的实现.参见 Matt Austern 的 完整示例.
By using the typedefs for the underlying types, all the iterator methods can have an identical implementation. See Matt Austern's complete example.
相关文章