Factor out `ArrowProxy`, a common implementation of `iterator::pointer` when
`iterator::reference` is not a true reference.

Move `ReferencePair` from `csv_record.h` to `iterable.h`. It is not specific for
`CsvRecord` but applicable to any map type with separate storage for keys and
values.

Minor changes to `{Linear,Chunked}SortedStringSet` iterators to satisfy C++20
requirements:

* Make `ChunkedSortedStringSet` an alias to an internal `IteratorImpl`
  instantiation instead of deriving from it. This makes return types correct.

* Let
  `{Linear,Chunked}SortedStringSet::NextInsertIterator::reference::operator=`
  be const (this is shallow), as required by `std::indirectly_writable`.

PiperOrigin-RevId: 883153178
diff --git a/riegeli/base/BUILD b/riegeli/base/BUILD
index 748ee98..82cc3f9 100644
--- a/riegeli/base/BUILD
+++ b/riegeli/base/BUILD
@@ -49,6 +49,7 @@
     hdrs = ["iterable.h"],
     deps = [
         ":type_traits",
+        "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/base:nullability",
     ],
 )
@@ -553,6 +554,7 @@
         ":cord_utils",
         ":external_data",
         ":initializer",
+        ":iterable",
         ":memory_estimator",
         ":new_aligned",
         ":null_safe_memcpy",
@@ -595,6 +597,7 @@
         ":external_data",
         ":external_ref",
         ":global",
+        ":iterable",
         ":shared_buffer",
         ":types",
         "@com_google_absl//absl/base:core_headers",
@@ -643,6 +646,7 @@
         ":bytes_ref",
         ":compact_string",
         ":compare",
+        ":iterable",
         ":type_traits",
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/strings:string_view",
diff --git a/riegeli/base/byte_fill.h b/riegeli/base/byte_fill.h
index 9b35ba5..f003b65 100644
--- a/riegeli/base/byte_fill.h
+++ b/riegeli/base/byte_fill.h
@@ -34,6 +34,7 @@
 #include "riegeli/base/chain.h"
 #include "riegeli/base/compare.h"
 #include "riegeli/base/external_data.h"
+#include "riegeli/base/iterable.h"
 #include "riegeli/base/shared_buffer.h"
 #include "riegeli/base/types.h"
 
@@ -290,18 +291,9 @@
   using iterator_category = std::input_iterator_tag;
   using value_type = BlockRef;
   using reference = value_type;
+  using pointer = ArrowProxy<reference>;
   using difference_type = ptrdiff_t;
 
-  class pointer {
-   public:
-    const reference* operator->() const { return &ref_; }
-
-   private:
-    friend class BlockIterator;
-    explicit pointer(reference ref) : ref_(ref) {}
-    reference ref_;
-  };
-
   BlockIterator() = default;
 
   BlockIterator(const BlockIterator& that) = default;
diff --git a/riegeli/base/chain_details.h b/riegeli/base/chain_details.h
index 2dcc40d..d48c477 100644
--- a/riegeli/base/chain_details.h
+++ b/riegeli/base/chain_details.h
@@ -45,6 +45,7 @@
 #include "riegeli/base/external_ref_support.h"
 #include "riegeli/base/initializer.h"
 #include "riegeli/base/intrusive_shared_ptr.h"
+#include "riegeli/base/iterable.h"
 #include "riegeli/base/memory_estimator.h"
 #include "riegeli/base/new_aligned.h"
 #include "riegeli/base/ownership.h"
@@ -182,18 +183,9 @@
   using iterator_category = std::input_iterator_tag;
   using value_type = BlockRef;
   using reference = value_type;
+  using pointer = ArrowProxy<reference>;
   using difference_type = ptrdiff_t;
 
-  class pointer {
-   public:
-    const reference* operator->() const { return &ref_; }
-
-   private:
-    friend class BlockIterator;
-    explicit pointer(reference ref) : ref_(ref) {}
-    reference ref_;
-  };
-
   BlockIterator() = default;
 
   explicit BlockIterator(const Chain* chain ABSL_ATTRIBUTE_LIFETIME_BOUND,
diff --git a/riegeli/base/iterable.h b/riegeli/base/iterable.h
index bac149c..e73ddcc 100644
--- a/riegeli/base/iterable.h
+++ b/riegeli/base/iterable.h
@@ -21,6 +21,7 @@
 #include <type_traits>
 #include <utility>
 
+#include "absl/base/attributes.h"
 #include "absl/base/nullability.h"
 #include "riegeli/base/type_traits.h"
 
@@ -259,6 +260,92 @@
 // `size(iterable)` after `using std::size;`.
 using iterable_internal::IterableHasSize;
 
+// Represents the result of `operator->` if `operator*` returns a proxy
+// object rather than a true reference. In particular this can be used as
+// `iterator::pointer` if `iterator::reference` is not a true reference.
+template <typename Reference>
+class ArrowProxy {
+ public:
+  explicit ArrowProxy(Reference ref) : ref_(std::move(ref)) {}
+
+  ArrowProxy(const ArrowProxy& that) = default;
+  ArrowProxy& operator=(const ArrowProxy& that) = default;
+
+  ArrowProxy(ArrowProxy&& that) noexcept = default;
+  ArrowProxy& operator=(ArrowProxy&& that) noexcept = default;
+
+  const Reference* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return &ref_;
+  }
+
+ private:
+  Reference ref_;
+};
+
+// A pair-like type to be used as `iterator::reference` for iterators over a map
+// with a separate storage for keys and values. In C++20 this lets the iterator
+// satisfy `std::indirectly_readable`.
+//
+// It extends `std::pair<T1, T2>` with conversions from `std::pair<U1, U2>&`
+// and with `std::basic_common_reference` specializations.
+//
+// Since C++23, `std::pair<T1, T2>` can be used directly instead.
+template <typename T1, typename T2>
+class ReferencePair : public std::pair<T1, T2> {
+ public:
+  using ReferencePair::pair::pair;
+
+  template <
+      class U1, class U2,
+      std::enable_if_t<
+          std::conjunction_v<
+              std::is_constructible<T1, U1&>, std::is_constructible<T2, U2&>,
+              std::negation<std::conjunction<std::is_convertible<U1&, T1>,
+                                             std::is_convertible<U2&, T2>>>>,
+          int> = 0>
+  explicit constexpr ReferencePair(std::pair<U1, U2>& p)
+      : ReferencePair::pair(p.first, p.second) {}
+
+  template <class U1, class U2,
+            std::enable_if_t<std::conjunction_v<std::is_convertible<U1&, T1>,
+                                                std::is_convertible<U2&, T2>>,
+                             int> = 0>
+  /*implicit*/ constexpr ReferencePair(std::pair<U1, U2>& p)
+      : ReferencePair::pair(p.first, p.second) {}
+};
+
 }  // namespace riegeli
 
+#if __cplusplus >= 202002L
+
+template <typename T1, typename T2, typename U1, typename U2,
+          template <typename> class TQual, template <typename> class UQual>
+struct std::basic_common_reference<riegeli::ReferencePair<T1, T2>,
+                                   std::pair<U1, U2>, TQual, UQual> {
+  using type =
+      riegeli::ReferencePair<std::common_reference_t<TQual<T1>, UQual<U1>>,
+                             std::common_reference_t<TQual<T2>, UQual<U2>>>;
+};
+
+template <typename T1, typename T2, typename U1, typename U2,
+          template <typename> class TQual, template <typename> class UQual>
+struct std::basic_common_reference<
+    std::pair<T1, T2>, riegeli::ReferencePair<U1, U2>, TQual, UQual> {
+  using type =
+      riegeli::ReferencePair<std::common_reference_t<TQual<T1>, UQual<U1>>,
+                             std::common_reference_t<TQual<T2>, UQual<U2>>>;
+};
+
+template <typename T1, typename T2, typename U1, typename U2,
+          template <typename> class TQual, template <typename> class UQual>
+struct std::basic_common_reference<riegeli::ReferencePair<T1, T2>,
+                                   riegeli::ReferencePair<U1, U2>, TQual,
+                                   UQual> {
+  using type =
+      riegeli::ReferencePair<std::common_reference_t<TQual<T1>, UQual<U1>>,
+                             std::common_reference_t<TQual<T2>, UQual<U2>>>;
+};
+
+#endif
+
 #endif  // RIEGELI_BASE_ITERABLE_H_
diff --git a/riegeli/base/optional_compact_string.h b/riegeli/base/optional_compact_string.h
index bc67524..a6bf69e 100644
--- a/riegeli/base/optional_compact_string.h
+++ b/riegeli/base/optional_compact_string.h
@@ -27,6 +27,7 @@
 #include "riegeli/base/bytes_ref.h"
 #include "riegeli/base/compact_string.h"
 #include "riegeli/base/compare.h"
+#include "riegeli/base/iterable.h"
 #include "riegeli/base/type_traits.h"
 
 namespace riegeli {
@@ -39,17 +40,6 @@
 // `const char*`, but not as `CompactString`, except by copying or moving from.
 class ABSL_ATTRIBUTE_TRIVIAL_ABI OptionalCompactString
     : public WithCompare<OptionalCompactString> {
- private:
-  class StringViewPointer {
-   public:
-    const absl::string_view* operator->() const { return &ref_; }
-
-   private:
-    friend class OptionalCompactString;
-    explicit StringViewPointer(absl::string_view ref) : ref_(ref) {}
-    absl::string_view ref_;
-  };
-
  public:
   // Creates a null `OptionalCompactString`.
   OptionalCompactString() = default;
@@ -137,11 +127,12 @@
     return CompactString::ViewFromRaw(&repr_);
   }
 
-  StringViewPointer operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+  ArrowProxy<absl::string_view> operator->() const
+      ABSL_ATTRIBUTE_LIFETIME_BOUND {
     RIEGELI_ASSERT(*this != nullptr)
         << "Failed precondition of OptionalCompactString::operator->: "
            "OptionalCompactString is nullptr";
-    return StringViewPointer(**this);
+    return ArrowProxy<absl::string_view>(**this);
   }
 
   // Ensures that the value is NUL-terminated after its size and returns
diff --git a/riegeli/containers/chunked_sorted_string_set.h b/riegeli/containers/chunked_sorted_string_set.h
index 1a03d9a..0ab02c4 100644
--- a/riegeli/containers/chunked_sorted_string_set.h
+++ b/riegeli/containers/chunked_sorted_string_set.h
@@ -94,9 +94,9 @@
     size_t size_hint_ = 0;
   };
 
-  class Iterator;
   using SplitElement = LinearSortedStringSet::SplitElement;
-  class SplitElementIterator;
+  using SplitElementIterator =
+      IteratorImpl<LinearSortedStringSet::SplitElementIterator>;
   class SplitElements;
   class Builder;
   class NextInsertIterator;
@@ -169,7 +169,7 @@
   using value_type = absl::string_view;
   using reference = value_type;
   using const_reference = reference;
-  using iterator = Iterator;
+  using iterator = IteratorImpl<LinearSortedStringSet::iterator>;
   using const_iterator = iterator;
   using size_type = size_t;
   using difference_type = ptrdiff_t;
@@ -227,10 +227,10 @@
       default;
 
   // Iteration over the set.
-  Iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
-  Iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
-  Iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
-  Iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // Returns a proxy for `LinearSortedStringSet` where each element is
   // represented as `SplitElement` rather than `absl::string_view`. This is
@@ -334,7 +334,8 @@
 
 // Iterates over a `LinearSortedStringSet` in the sorted order.
 template <typename LinearIterator>
-class ChunkedSortedStringSet::IteratorImpl {
+class ChunkedSortedStringSet::IteratorImpl
+    : public WithEqual<IteratorImpl<LinearIterator>> {
  public:
   // `iterator_concept` is only `std::input_iterator_tag` because the
   // `std::forward_iterator` requirement and above require references to remain
@@ -346,18 +347,9 @@
   using iterator_category = std::input_iterator_tag;
   using value_type = typename LinearIterator::value_type;
   using reference = value_type;
+  using pointer = ArrowProxy<reference>;
   using difference_type = ptrdiff_t;
 
-  class pointer {
-   public:
-    const reference* operator->() const { return &ref_; }
-
-   private:
-    friend class IteratorImpl<LinearIterator>;
-    explicit pointer(const reference& ref) : ref_(ref) {}
-    const reference& ref_;
-  };
-
   // A sentinel value, equal to `end()`.
   IteratorImpl() = default;
 
@@ -395,8 +387,10 @@
     return tmp;
   }
 
- protected:
-  static bool Equal(const IteratorImpl& a, const IteratorImpl& b) {
+  // Iterators can be compared even if they are associated with different
+  // `ChunkedSortedStringSet` objects. All `end()` values are equal, while all
+  // other values are not equal.
+  friend bool operator==(const IteratorImpl& a, const IteratorImpl& b) {
     return a.current_iterator_ == b.current_iterator_;
   }
 
@@ -421,35 +415,6 @@
   const ChunkedSortedStringSet* set_ = nullptr;
 };
 
-class ChunkedSortedStringSet::Iterator
-    : public IteratorImpl<LinearSortedStringSet::Iterator>,
-      public WithEqual<Iterator> {
- public:
-  using Iterator::IteratorImpl::IteratorImpl;
-
-  // Iterators can be compared even if they are associated with different
-  // `ChunkedSortedStringSet` objects. All `end()` values are equal, while all
-  // other values are not equal.
-  friend bool operator==(const Iterator& a, const Iterator& b) {
-    return Equal(a, b);
-  }
-};
-
-class ChunkedSortedStringSet::SplitElementIterator
-    : public IteratorImpl<LinearSortedStringSet::SplitElementIterator>,
-      public WithEqual<SplitElementIterator> {
- public:
-  using SplitElementIterator::IteratorImpl::IteratorImpl;
-
-  // Iterators can be compared even if they are associated with different
-  // `ChunkedSortedStringSet` objects. All `end()` values are equal, while all
-  // other values are not equal.
-  friend bool operator==(const SplitElementIterator& a,
-                         const SplitElementIterator& b) {
-    return Equal(a, b);
-  }
-};
-
 // A proxy for `ChunkedSortedStringSet` where each element is represented as
 // `SplitElement` rather than `absl::string_view`. This is more efficient but
 // less convenient.
@@ -587,8 +552,8 @@
   using iterator_concept = std::output_iterator_tag;
   using iterator_category = std::output_iterator_tag;
   using value_type = absl::string_view;
-  using difference_type = ptrdiff_t;
   using pointer = void;
+  using difference_type = ptrdiff_t;
 
   class reference {
    public:
@@ -597,13 +562,13 @@
     // `std::string&&` is accepted with a template to avoid implicit conversions
     // to `std::string` which can be ambiguous against `absl::string_view`
     // (e.g. `const char*`).
-    reference& operator=(absl::string_view element) {
+    const reference& operator=(absl::string_view element) const {
       builder_->InsertNext(element);
       return *this;
     }
     template <typename Element,
               std::enable_if_t<std::is_same_v<Element, std::string>, int> = 0>
-    reference& operator=(Element&& element) {
+    const reference& operator=(Element&& element) const {
       // `std::move(element)` is correct and `std::forward<Element>(element)` is
       // not necessary: `Element` is always `std::string`, never an lvalue
       // reference.
@@ -691,22 +656,22 @@
   return builder.Build();
 }
 
-inline ChunkedSortedStringSet::Iterator ChunkedSortedStringSet::begin() const
+inline ChunkedSortedStringSet::iterator ChunkedSortedStringSet::begin() const
     ABSL_ATTRIBUTE_LIFETIME_BOUND {
-  return Iterator(this);
+  return iterator(this);
 }
 
-inline ChunkedSortedStringSet::Iterator ChunkedSortedStringSet::cbegin() const
+inline ChunkedSortedStringSet::iterator ChunkedSortedStringSet::cbegin() const
     ABSL_ATTRIBUTE_LIFETIME_BOUND {
   return begin();
 }
 
-inline ChunkedSortedStringSet::Iterator ChunkedSortedStringSet::end() const
+inline ChunkedSortedStringSet::iterator ChunkedSortedStringSet::end() const
     ABSL_ATTRIBUTE_LIFETIME_BOUND {
-  return Iterator();
+  return iterator();
 }
 
-inline ChunkedSortedStringSet::Iterator ChunkedSortedStringSet::cend() const
+inline ChunkedSortedStringSet::iterator ChunkedSortedStringSet::cend() const
     ABSL_ATTRIBUTE_LIFETIME_BOUND {
   return end();
 }
diff --git a/riegeli/containers/linear_sorted_string_set.h b/riegeli/containers/linear_sorted_string_set.h
index aa563a9..6baa660 100644
--- a/riegeli/containers/linear_sorted_string_set.h
+++ b/riegeli/containers/linear_sorted_string_set.h
@@ -296,18 +296,9 @@
   using iterator_category = std::input_iterator_tag;
   using value_type = absl::string_view;
   using reference = value_type;
+  using pointer = ArrowProxy<reference>;
   using difference_type = ptrdiff_t;
 
-  class pointer {
-   public:
-    const reference* operator->() const { return &ref_; }
-
-   private:
-    friend class Iterator;
-    explicit pointer(const reference& ref) : ref_(ref) {}
-    const reference& ref_;
-  };
-
   // A sentinel value, equal to `end()`.
   Iterator() = default;
 
@@ -481,18 +472,9 @@
   using iterator_category = std::input_iterator_tag;
   using value_type = SplitElement;
   using reference = value_type;
+  using pointer = ArrowProxy<reference>;
   using difference_type = ptrdiff_t;
 
-  class pointer {
-   public:
-    const reference* operator->() const { return &ref_; }
-
-   private:
-    friend class SplitElementIterator;
-    explicit pointer(const reference& ref) : ref_(ref) {}
-    const reference& ref_;
-  };
-
   // A sentinel value, equal to `end()`.
   SplitElementIterator() = default;
 
@@ -708,13 +690,13 @@
     // `std::string&&` is accepted with a template to avoid implicit conversions
     // to `std::string` which can be ambiguous against `absl::string_view`
     // (e.g. `const char*`).
-    reference& operator=(absl::string_view element) {
+    const reference& operator=(absl::string_view element) const {
       builder_->InsertNext(element);
       return *this;
     }
     template <typename Element,
               std::enable_if_t<std::is_same_v<Element, std::string>, int> = 0>
-    reference& operator=(Element&& element) {
+    const reference& operator=(Element&& element) const {
       // `std::move(element)` is correct and `std::forward<Element>(element)` is
       // not necessary: `Element` is always `std::string`, never an lvalue
       // reference.
diff --git a/riegeli/csv/csv_record.h b/riegeli/csv/csv_record.h
index 058fa67..b36db4c 100644
--- a/riegeli/csv/csv_record.h
+++ b/riegeli/csv/csv_record.h
@@ -51,36 +51,6 @@
 
 namespace riegeli::csv_internal {
 
-// A pair-like type which supports C++20 `std::common_reference` with similar
-// enough pairs. This is needed for `CsvRecord::{,const_}iterator` to satisfy
-// C++20 input iterator requirements.
-//
-// Since C++23, `std::pair<T1, T2>` can be used directly instead, because it has
-// the necessary conversions and `std::basic_common_reference` specializations.
-template <typename T1, typename T2>
-class ReferencePair : public std::pair<T1, T2> {
- public:
-  using ReferencePair::pair::pair;
-
-  template <
-      class U1, class U2,
-      std::enable_if_t<
-          std::conjunction_v<
-              std::is_constructible<T1, U1&>, std::is_constructible<T2, U2&>,
-              std::negation<std::conjunction<std::is_convertible<U1&, T1>,
-                                             std::is_convertible<U2&, T2>>>>,
-          int> = 0>
-  explicit constexpr ReferencePair(std::pair<U1, U2>& p)
-      : ReferencePair::pair(p.first, p.second) {}
-
-  template <class U1, class U2,
-            std::enable_if_t<std::conjunction_v<std::is_convertible<U1&, T1>,
-                                                std::is_convertible<U2&, T2>>,
-                             int> = 0>
-  /*implicit*/ constexpr ReferencePair(std::pair<U1, U2>& p)
-      : ReferencePair::pair(p.first, p.second) {}
-};
-
 // `ToStringVector()` converts an iterable of elements convertible to
 // `absl::string_view` to a `std::vector<std::string>`.
 
@@ -96,40 +66,6 @@
 
 }  // namespace riegeli::csv_internal
 
-#if __cplusplus >= 202002L
-
-template <typename T1, typename T2, template <typename> class TQual,
-          template <typename> class UQual>
-struct std::basic_common_reference<riegeli::csv_internal::ReferencePair<T1, T2>,
-                                   std::pair<std::string, std::string>, TQual,
-                                   UQual> {
-  using type = riegeli::csv_internal::ReferencePair<
-      std::common_reference_t<TQual<T1>, UQual<std::string>>,
-      std::common_reference_t<TQual<T2>, UQual<std::string>>>;
-};
-
-template <typename T1, typename T2, template <typename> class TQual,
-          template <typename> class UQual>
-struct std::basic_common_reference<std::pair<std::string, std::string>,
-                                   riegeli::csv_internal::ReferencePair<T1, T2>,
-                                   TQual, UQual> {
-  using type = riegeli::csv_internal::ReferencePair<
-      std::common_reference_t<TQual<std::string>, UQual<T1>>,
-      std::common_reference_t<TQual<std::string>, UQual<T2>>>;
-};
-
-template <typename T1, typename T2, typename U1, typename U2,
-          template <typename> class TQual, template <typename> class UQual>
-struct std::basic_common_reference<riegeli::csv_internal::ReferencePair<T1, T2>,
-                                   riegeli::csv_internal::ReferencePair<U1, U2>,
-                                   TQual, UQual> {
-  using type = riegeli::csv_internal::ReferencePair<
-      std::common_reference_t<TQual<T1>, UQual<U1>>,
-      std::common_reference_t<TQual<T2>, UQual<U2>>>;
-};
-
-#endif
-
 namespace riegeli {
 
 // A normalizer for `CsvHeader` and `CsvReaderBase::Options::set_normalizer()`,
@@ -622,22 +558,13 @@
     // `LegacyForwardIterator` requirement and above require `reference` to be
     // a true reference type.
     using iterator_category = std::input_iterator_tag;
-    using value_type = std::pair<std::string, std::string>;
-    using reference = csv_internal::ReferencePair<
-        const std::string&,
-        typename std::iterator_traits<FieldIterator>::reference>;
+    using value_type = std::pair<const std::string, std::string>;
+    using reference =
+        ReferencePair<const std::string&,
+                      typename std::iterator_traits<FieldIterator>::reference>;
+    using pointer = ArrowProxy<reference>;
     using difference_type = ptrdiff_t;
 
-    class pointer {
-     public:
-      const reference* operator->() const { return &ref_; }
-
-     private:
-      friend class IteratorImpl<FieldIterator>;
-      explicit pointer(reference ref) : ref_(ref) {}
-      reference ref_;
-    };
-
     IteratorImpl() = default;
 
     // Conversion from `iterator` to `const_iterator`.
@@ -695,11 +622,9 @@
  public:
   using key_type = std::string;
   using mapped_type = std::string;
-  using value_type = std::pair<std::string, std::string>;
-  using reference =
-      csv_internal::ReferencePair<const std::string&, std::string&>;
-  using const_reference =
-      csv_internal::ReferencePair<const std::string&, const std::string&>;
+  using value_type = std::pair<const std::string, std::string>;
+  using reference = ReferencePair<const std::string&, std::string&>;
+  using const_reference = ReferencePair<const std::string&, const std::string&>;
   using iterator = IteratorImpl<std::vector<std::string>::iterator>;
   using const_iterator = IteratorImpl<std::vector<std::string>::const_iterator>;
   using reverse_iterator = std::reverse_iterator<iterator>;