为何STL common_iterator的operator*()方法不总是const限定?
关于libstdc++中
common_iterator双operator*()的疑问 先看libstdc++里common_iterator的部分实现:
class common_iterator { ... [[nodiscard]] constexpr decltype(auto) operator*() { __glibcxx_assert(_M_index == 0); return *_M_it; } [[nodiscard]] constexpr decltype(auto) operator*() const requires __detail::__dereferenceable<const _It> { __glibcxx_assert(_M_index == 0); return *_M_it; } ... }
为什么不能只保留const限定的operator*()?
如果只提供一个const版本的operator*(),所有解引用操作都会走这个重载,但问题在于不是所有迭代器的const限定版本都支持解引用。
当你操作const common_iterator<It>时,内部存储的迭代器_M_it会被视为const It类型。这时候能不能解引用const It,完全由底层迭代器It的特性决定:
- 对于输入、前向、双向、随机访问这类迭代器,
const It通常可以正常解引用; - 但对于输出迭代器(比如
ostream_iterator),它的operator*()语义是返回一个代理对象用于写入数据。const ostream_iterator的operator*()没有实际意义——你无法给const的代理对象赋值,也就完成不了输出操作,这类迭代器的const版本本身就不支持解引用。
所以common_iterator必须拆分两个重载:非const版本直接对_M_it解引用,const版本则通过requires __detail::__dereferenceable<const _It>做限定,只有当底层迭代器的const版本确实支持解引用时,才提供这个重载。
为什么持有普通迭代器的const引用时,有时无法解引用?
当你持有const common_iterator<It>(其中It是普通迭代器,非const_iterator),如果const It不满足__detail::__dereferenceable这个约束,const版本的operator*()就不会被实例化。此时编译器找不到匹配的解引用重载,自然无法执行解引用操作。
哪些场景下禁止解引用是合理的?
最典型的场景就是底层迭代器为输出迭代器时:
- 比如
ostream_iterator,它的核心作用是向流中写入数据,解引用后的代理对象需要被赋值才能完成写入。如果是const的ostream_iterator,既不能修改迭代器本身,也无法通过解引用完成写入,此时禁止解引用完全符合迭代器的语义设计。 - 另外一些自定义迭代器,如果它们的const限定版本被设计为不支持解引用(比如某些仅允许写入的特殊迭代器),
common_iterator也会遵循这个设计,不提供const版本的operator*(),避免违反迭代器的语义约定。
内容的提问来源于stack exchange,提问作者phinz
相关产品推荐
相关产品推荐

