Add a `string_view` overload to `absl::StrJoin`

This allows users to pass a collection of string-like objects in an
`initializer_list` without having to convert everything to the same
type first.

`initializer_list` has the requirement that everything is copied in to
the list. For strings hitting the `string_view` overload avoids
unnecessary copies.

This may be a breaking change if `absl::StrJoin` has an explicit
template parameter, for example `absl::StrJoin<std::string>({foo,
"bar"});`. In this case, remove the explicit template parameter.

PiperOrigin-RevId: 633295575
Change-Id: Ie5f0860f409f639a27a58949842ec961e0b3bdeb
diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h
index 6a92c0f..badc944 100644
--- a/absl/strings/str_join.h
+++ b/absl/strings/str_join.h
@@ -247,12 +247,20 @@
   return strings_internal::JoinRange(range, separator, fmt);
 }
 
-template <typename T, typename Formatter>
+template <typename T, typename Formatter,
+          typename = typename std::enable_if<
+              !std::is_convertible<T, absl::string_view>::value>::type>
 std::string StrJoin(std::initializer_list<T> il, absl::string_view separator,
                     Formatter&& fmt) {
   return strings_internal::JoinRange(il, separator, fmt);
 }
 
+template <typename Formatter>
+inline std::string StrJoin(std::initializer_list<absl::string_view> il,
+                           absl::string_view separator, Formatter&& fmt) {
+  return strings_internal::JoinRange(il, separator, fmt);
+}
+
 template <typename... T, typename Formatter>
 std::string StrJoin(const std::tuple<T...>& value, absl::string_view separator,
                     Formatter&& fmt) {
@@ -269,9 +277,14 @@
   return strings_internal::JoinRange(range, separator);
 }
 
-template <typename T>
-std::string StrJoin(std::initializer_list<T> il,
-                    absl::string_view separator) {
+template <typename T, typename = typename std::enable_if<!std::is_convertible<
+                          T, absl::string_view>::value>::type>
+std::string StrJoin(std::initializer_list<T> il, absl::string_view separator) {
+  return strings_internal::JoinRange(il, separator);
+}
+
+inline std::string StrJoin(std::initializer_list<absl::string_view> il,
+                           absl::string_view separator) {
   return strings_internal::JoinRange(il, separator);
 }
 
diff --git a/absl/strings/str_join_test.cc b/absl/strings/str_join_test.cc
index 449f95b..cd52e11 100644
--- a/absl/strings/str_join_test.cc
+++ b/absl/strings/str_join_test.cc
@@ -428,6 +428,42 @@
   }
 }
 
+TEST(StrJoin, StringViewInitializerList) {
+  {
+    // Tests initializer_list of string_views
+    std::string b = "b";
+    EXPECT_EQ("a-b-c", absl::StrJoin({"a", b, "c"}, "-"));
+  }
+  {
+    // Tests initializer_list of string_views with a non-default formatter
+    TestingParenFormatter f;
+    std::string b = "b";
+    EXPECT_EQ("(a)-(b)-(c)", absl::StrJoin({"a", b, "c"}, "-", f));
+  }
+
+  class NoCopy {
+   public:
+    explicit NoCopy(absl::string_view view) : view_(view) {}
+    NoCopy(const NoCopy&) = delete;
+    operator absl::string_view() { return view_; }  // NOLINT
+   private:
+    absl::string_view view_;
+  };
+  {
+    // Tests initializer_list of string_views preferred over initializer_list<T>
+    // for T that is implicitly convertible to string_view
+    EXPECT_EQ("a-b-c",
+              absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-"));
+  }
+  {
+    // Tests initializer_list of string_views preferred over initializer_list<T>
+    // for T that is implicitly convertible to string_view
+    TestingParenFormatter f;
+    EXPECT_EQ("(a)-(b)-(c)",
+              absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-", f));
+  }
+}
+
 TEST(StrJoin, Tuple) {
   EXPECT_EQ("", absl::StrJoin(std::make_tuple(), "-"));
   EXPECT_EQ("hello", absl::StrJoin(std::make_tuple("hello"), "-"));