容器迭代器的设计问题及建议
Opened this issue · 0 comments
frederick-vs-ja commented
ADL 防御
考虑以下的 EvilPointer
类型:
struct Incomplete;
template<class T>
struct Holder {
T t;
};
using EvilPointer = Holder<Incomplete>*;
倘若我等试图将某个迭代器实现为类模板特化 SomeIterator<EvilPointer>
,则 i == i
会产生 ADL (实参依赖查找),进而要求关联类 Incomplete
完整,产生硬错误。换言之这样的实现不能满足迭代器的要求。
解决方案是将迭代器实现为类模板的非模板成员类,这可以避免用户提供的类型成为迭代器类型的关联类。
template<class T>
struct SomeContainerIteratorProvider {
struct Iter {
/* 具体实现 */
};
};
template<class T>
using SomeContainerIterator = typename SomeContainerIteratorProvider<T>::Iter; // C++20 起此处不需要 typename
参考资料
最小化依赖的迭代器类型
通常来说容器迭代器应该只依赖元素类型,以及分配器所确定的指针类型(可能为缀饰指针)。
分配器、比较器、哈希器最好不影响迭代器的类型。
对于 std::array
以及定长的 std::span
,编译时确定的长度最好不影响迭代器类型。但 std::array<T, 0>
的情况可能稍微特殊一点。
参考资料
比较运算符实现为隐藏友元
可以避免对其他类型查找运算符时该类的运算符参被纳入考虑,并且减少隐式转换的干扰。
const_iterator
与 iterator
的关系
隐式转换转换不应用继承实现,因为非 const 迭代器和 const 迭代器并没有 IS-A 关系。继承会允许经由基类子对象的赋值一个非 const 的迭代器指向本应只能由 const 迭代器访问的元素。
通过转换函数 operator T()
实现隐式转换可能会相对容易控制。