growing_range.h 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_COMMON_GROWING_RANGE_H_
  5. #define CARBON_COMMON_GROWING_RANGE_H_
  6. #include <ranges>
  7. namespace Carbon {
  8. // A range adaptor for a random-access container such as `std::vector` or
  9. // `llvm::SmallVector` that might have elements appended during the iteration.
  10. // This adaptor avoids invalidation issues by tracking an index instead of an
  11. // iterator, and by returning by value from `operator*` instead of by reference.
  12. //
  13. // This class is intended only for use as the range in a range-based for loop,
  14. // and as such does not provide a complete range or iterator interface. Instead,
  15. // it provides only the interface required by the range-based for loop.
  16. template <typename ContainerT>
  17. requires std::ranges::sized_range<ContainerT> &&
  18. std::ranges::random_access_range<ContainerT>
  19. class GrowingRange {
  20. public:
  21. // An end sentinel for the range.
  22. class End {};
  23. // An iterator into a potentially-growing range. Tracks the container and the
  24. // current index, and indexes the container on each dereference.
  25. class Iterator {
  26. public:
  27. // Dereferences the iterator. These intentionally don't return by reference,
  28. // to avoid handing out a reference that would be invalidated when the
  29. // container grows during the traversal.
  30. auto operator*() -> auto { return (*container_)[index_]; }
  31. auto operator*() const -> auto { return (*container_)[index_]; }
  32. friend auto operator!=(Iterator it, End /*end*/) -> bool {
  33. return it.index_ != it.container_->size();
  34. }
  35. auto operator++() -> void { ++index_; }
  36. private:
  37. friend class GrowingRange;
  38. explicit Iterator(const ContainerT* container)
  39. : container_(container), index_(0) {}
  40. const ContainerT* container_;
  41. size_t index_;
  42. };
  43. explicit GrowingRange(const ContainerT& container) : container_(&container) {}
  44. auto begin() const -> Iterator { return Iterator(container_); }
  45. auto end() const -> End { return {}; }
  46. private:
  47. const ContainerT* container_;
  48. };
  49. template <typename ContainerT>
  50. GrowingRange(const ContainerT&) -> GrowingRange<ContainerT>;
  51. } // namespace Carbon
  52. #endif // CARBON_COMMON_GROWING_RANGE_H_