pw_span: Fix deduction guides for containers

Previously, the constness of the container implied the constness of the
elements, which is not always correct. A mutable std::string has mutable
elements, but a mutable std::string_view has const elements. The result
was that template argument deduction failed in some cases, including a
mutable std::string_view.

This change has the container deduction guides use the actual element
type from the container as the element type for the span.

Change-Id: Idafde58b8cfedf86e1f90a3e22fb05780903222e
diff --git a/pw_span/public/pw_span/span.h b/pw_span/public/pw_span/span.h
index 4f85eb1..2ea01ff 100644
--- a/pw_span/public/pw_span/span.h
+++ b/pw_span/public/pw_span/span.h
@@ -465,11 +465,22 @@
 template <class T, std::size_t N>
 span(const std::array<T, N>&) -> span<const T, N>;
 
+namespace internal {
+
+// Containers can be mutable or const and have mutable or const members. Check
+// the type of the accessed elements to determine which type of span should be
+// created (e.g. span<char> or span<const char>).
+template <typename T>
+using ValueType = std::remove_reference_t<decltype(std::declval<T>()[0])>;
+
+}  // namespace internal
+
+// This diverges a little from the standard, which uses std::ranges.
 template <class Container>
-span(Container&) -> span<typename Container::value_type>;
+span(Container&) -> span<internal::ValueType<Container>>;
 
 template <class Container>
-span(const Container&) -> span<const typename Container::value_type>;
+span(const Container&) -> span<internal::ValueType<const Container>>;
 
 #endif  // __cpp_deduction_guides
 
diff --git a/pw_span/span_test.cc b/pw_span/span_test.cc
index f4770f6..37875f6 100644
--- a/pw_span/span_test.cc
+++ b/pw_span/span_test.cc
@@ -58,7 +58,6 @@
 
 }  // namespace
 
-// Pigweed: Test deducing from std::string_view.
 TEST(SpanTest, DeductionGuides_MutableArray) {
   char array[] = {'a', 'b', 'c', 'd', '\0'};
 
@@ -101,27 +100,112 @@
   EXPECT_STREQ(the_span.data(), "abcd");
 }
 
-TEST(SpanTest, DeductionGuides_MutableContainer) {
-  std::vector<int> foo = {3456};
-
-  auto the_span = span(foo);
-  static_assert(the_span.extent == dynamic_extent);
-
-  EXPECT_EQ(foo[0], the_span[0]);
-  EXPECT_EQ(foo.size(), the_span.size());
-
-  the_span[0] = 9876;
-  EXPECT_EQ(9876, foo[0]);
-}
-
-TEST(SpanTest, DeductionGuides_ConstContainer) {
-  auto the_span = span(std::string_view("Hello"));
+TEST(SpanTest, DeductionGuides_MutableContainerWithConstElements) {
+  std::string_view string("Hello");
+  auto the_span = span(string);
   static_assert(the_span.extent == dynamic_extent);
 
   EXPECT_STREQ("Hello", the_span.data());
   EXPECT_EQ(5u, the_span.size());
 }
 
+TEST(SpanTest, DeductionGuides_MutableContainerWithMutableElements) {
+  std::string string("Hello");
+  auto the_span = span(string);
+  static_assert(the_span.extent == dynamic_extent);
+
+  EXPECT_EQ(5u, the_span.size());
+  the_span[1] = 'a';
+  EXPECT_STREQ(the_span.data(), string.data());
+  EXPECT_STREQ("Hallo", the_span.data());
+}
+
+class MutableStringView {
+ public:
+  using element_type = char;
+  using value_type = char;
+  using size_type = size_t;
+  using difference_type = ptrdiff_t;
+  using pointer = char*;
+  using reference = char&;
+  using iterator = char*;
+  using const_iterator = const char*;
+  using reverse_iterator = std::reverse_iterator<iterator>;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+  MutableStringView(char* str) : data_(str, std::strlen(str)) {}
+
+  char& operator[](size_type index) const { return data_[index]; }
+  pointer data() const { return data_.data(); }
+  size_type size() const { return data_.size(); }
+  iterator begin() const { return data_.begin(); }
+  iterator end() const { return data_.end(); }
+
+ private:
+  span<char> data_;
+};
+
+TEST(SpanTest, DeductionGuides_ConstContainerWithMutableElements) {
+  char data[] = "54321";
+  MutableStringView view(data);
+
+  auto the_span = span(view);
+  static_assert(the_span.extent == dynamic_extent);
+
+  EXPECT_EQ(5u, the_span.size());
+  view[2] = '?';
+  EXPECT_STREQ("54?21", the_span.data());
+  EXPECT_STREQ("54?21", data);
+}
+
+TEST(SpanTest, DeductionGuides_ConstContainerWithMutableValueType) {
+  const std::string string("Hello");
+  auto the_span = span(string);
+  static_assert(the_span.extent == dynamic_extent);
+
+  EXPECT_EQ(5u, the_span.size());
+  EXPECT_STREQ("Hello", the_span.data());
+}
+
+TEST(SpanTest, DeductionGuides_ConstContainerWithConstElements) {
+  const std::string_view string("Hello");
+  auto the_span = span(string);
+  static_assert(the_span.extent == dynamic_extent);
+
+  EXPECT_EQ(5u, the_span.size());
+  EXPECT_STREQ("Hello", the_span.data());
+}
+
+TEST(SpanTest, DeductionGuides_FromTemporary_ContainerWithConstElements) {
+  auto the_span = span(std::string_view("Hello"));
+  static_assert(the_span.extent == dynamic_extent);
+
+  EXPECT_EQ(5u, the_span.size());
+  EXPECT_STREQ("Hello", the_span.data());
+}
+
+TEST(SpanTest, DeductionGuides_FromReference) {
+  std::array<int, 5> array{1, 3, 5, 7, 9};
+  std::array<int, 5>& array_ref = array;
+
+  auto the_span = span(array_ref);
+  static_assert(the_span.extent == 5);
+
+  for (unsigned i = 0; i < array.size(); ++i) {
+    ASSERT_EQ(array[i], the_span[i]);
+  }
+}
+
+TEST(SpanTest, DeductionGuides_FromConstReference) {
+  std::string_view string = "yo!";
+  const std::string_view& string_ref = string;
+
+  auto the_span = span(string_ref);
+  static_assert(the_span.extent == dynamic_extent);
+
+  EXPECT_EQ(string, the_span.data());
+}
+
 TEST(SpanTest, DefaultConstructor) {
   span<int> dynamic_span;
   EXPECT_EQ(nullptr, dynamic_span.data());