Mq-b/c-plus-plus

容器迭代器的设计问题及建议

Opened this issue · 0 comments

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_iteratoriterator 的关系

隐式转换转换不应用继承实现,因为非 const 迭代器和 const 迭代器并没有 IS-A 关系。继承会允许经由基类子对象的赋值一个非 const 的迭代器指向本应只能由 const 迭代器访问的元素。

通过转换函数 operator T() 实现隐式转换可能会相对容易控制。

参考资料