Support std::string_view when compiled under C++17
diff --git a/docs/advanced/cast/overview.rst b/docs/advanced/cast/overview.rst
index 49781dc..2ac7d30 100644
--- a/docs/advanced/cast/overview.rst
+++ b/docs/advanced/cast/overview.rst
@@ -116,6 +116,9 @@
 +------------------------------------+---------------------------+-------------------------------+
 | ``std::wstring``                   | STL dynamic wide string   | :file:`pybind11/pybind11.h`   |
 +------------------------------------+---------------------------+-------------------------------+
+| ``std::string_view``,              | STL C++17 string views    | :file:`pybind11/pybind11.h`   |
+| ``std::u16string_view``, etc.      |                           |                               |
++------------------------------------+---------------------------+-------------------------------+
 | ``std::pair<T1, T2>``              | Pair of two custom types  | :file:`pybind11/pybind11.h`   |
 +------------------------------------+---------------------------+-------------------------------+
 | ``std::tuple<...>``                | Arbitrary tuple of types  | :file:`pybind11/pybind11.h`   |
diff --git a/docs/advanced/cast/strings.rst b/docs/advanced/cast/strings.rst
index 0c994f6..864373e 100644
--- a/docs/advanced/cast/strings.rst
+++ b/docs/advanced/cast/strings.rst
@@ -287,6 +287,15 @@
 no way to capture them in a C++ character type.
 
 
+C++17 string views
+==================
+
+C++17 string views are automatically supported when compiling in C++17 mode.
+They follow the same rules for encoding and decoding as the corresponding STL
+string type (for example, a ``std::u16string_view`` argument will be passed
+UTF-16-encoded data, and a returned ``std::string_view`` will be decoded as
+UTF-8).
+
 References
 ==========
 
diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h
index 78fb13c..fbca3c6 100644
--- a/include/pybind11/cast.h
+++ b/include/pybind11/cast.h
@@ -18,6 +18,19 @@
 #include <tuple>
 #include <cstring>
 
+#if defined(PYBIND11_CPP17)
+#  if defined(__has_include)
+#    if __has_include(<string_view>)
+#      define PYBIND11_HAS_STRING_VIEW
+#    endif
+#  elif defined(_MSC_VER)
+#    define PYBIND11_HAS_STRING_VIEW
+#  endif
+#endif
+#ifdef PYBIND11_HAS_STRING_VIEW
+#include <string_view>
+#endif
+
 NAMESPACE_BEGIN(pybind11)
 NAMESPACE_BEGIN(detail)
 // Forward declarations:
@@ -1003,10 +1016,11 @@
 };
 
 // Helper class for UTF-{8,16,32} C++ stl strings:
-template <typename CharT, class Traits, class Allocator>
-struct type_caster<std::basic_string<CharT, Traits, Allocator>, enable_if_t<is_std_char_type<CharT>::value>> {
+template <typename StringType, bool IsView = false> struct string_caster {
+    using CharT = typename StringType::value_type;
+
     // Simplify life by being able to assume standard char sizes (the standard only guarantees
-    // minimums), but Python requires exact sizes
+    // minimums, but Python requires exact sizes)
     static_assert(!std::is_same<CharT, char>::value || sizeof(CharT) == 1, "Unsupported char size != 1");
     static_assert(!std::is_same<CharT, char16_t>::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2");
     static_assert(!std::is_same<CharT, char32_t>::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4");
@@ -1015,8 +1029,6 @@
             "Unsupported wchar_t size != 2/4");
     static constexpr size_t UTF_N = 8 * sizeof(CharT);
 
-    using StringType = std::basic_string<CharT, Traits, Allocator>;
-
     bool load(handle src, bool) {
 #if PY_MAJOR_VERSION < 3
         object temp;
@@ -1050,11 +1062,16 @@
         size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT);
         if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32
         value = StringType(buffer, length);
+
+        // If we're loading a string_view we need to keep the encoded Python object alive:
+        if (IsView)
+            view_into = std::move(utfNbytes);
+
         return true;
     }
 
     static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) {
-        const char *buffer = reinterpret_cast<const char *>(src.c_str());
+        const char *buffer = reinterpret_cast<const char *>(src.data());
         ssize_t nbytes = ssize_t(src.size() * sizeof(CharT));
         handle s = decode_utfN(buffer, nbytes);
         if (!s) throw error_already_set();
@@ -1064,6 +1081,8 @@
     PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME));
 
 private:
+    object view_into;
+
     static handle decode_utfN(const char *buffer, ssize_t nbytes) {
 #if !defined(PYPY_VERSION)
         return
@@ -1101,6 +1120,16 @@
     bool load_bytes(enable_if_t<sizeof(C) != 1, handle>) { return false; }
 };
 
+template <typename CharT, class Traits, class Allocator>
+struct type_caster<std::basic_string<CharT, Traits, Allocator>, enable_if_t<is_std_char_type<CharT>::value>>
+    : string_caster<std::basic_string<CharT, Traits, Allocator>> {};
+
+#ifdef PYBIND11_HAS_STRING_VIEW
+template <typename CharT, class Traits>
+struct type_caster<std::basic_string_view<CharT, Traits>, enable_if_t<is_std_char_type<CharT>::value>>
+    : string_caster<std::basic_string_view<CharT, Traits>, true> {};
+#endif
+
 // Type caster for C-style strings.  We basically use a std::string type caster, but also add the
 // ability to use None as a nullptr char* (which the string caster doesn't allow).
 template <typename CharT> struct type_caster<CharT, enable_if_t<is_std_char_type<CharT>::value>> {
diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp
index a3ed289..d130af6 100644
--- a/tests/test_python_types.cpp
+++ b/tests/test_python_types.cpp
@@ -555,6 +555,21 @@
     m.def("nodefer_none_optional", [](py::none) { return false; });
 #endif
 
+#ifdef PYBIND11_HAS_STRING_VIEW
+    m.attr("has_string_view") = true;
+    m.def("string_view_print",   [](std::string_view s)    { py::print(s, s.size()); });
+    m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); });
+    m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); });
+    m.def("string_view_chars",   [](std::string_view s)    { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; });
+    m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; });
+    m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; });
+    m.def("string_view_return",   []() { return std::string_view(u8"utf8 secret \U0001f382"); });
+    m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); });
+    m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); });
+#else
+    m.attr("has_string_view") = false;
+#endif
+
     m.def("return_capsule_with_destructor",
         []() {
             py::print("creating capsule");
diff --git a/tests/test_python_types.py b/tests/test_python_types.py
index ecd317e..2af9432 100644
--- a/tests/test_python_types.py
+++ b/tests/test_python_types.py
@@ -2,7 +2,8 @@
 import pytest
 import pybind11_tests
 
-from pybind11_tests import ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional
+from pybind11_tests import (ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional,
+                            has_string_view)
 
 
 def test_repr():
@@ -558,6 +559,48 @@
     assert string_length(u'💩'.encode("utf8")) == 4
 
 
+@pytest.mark.skipif(not has_string_view, reason='no <string_view>')
+def test_string_view(capture):
+    """Tests support for C++17 string_view arguments and return values"""
+    from pybind11_tests import (string_view_print, string_view16_print, string_view32_print,
+                                string_view_chars, string_view16_chars, string_view32_chars,
+                                string_view_return, string_view16_return, string_view32_return)
+
+    assert string_view_chars("Hi") == [72, 105]
+    assert string_view_chars("Hi 🎂") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82]
+    assert string_view16_chars("Hi 🎂") == [72, 105, 32, 0xd83c, 0xdf82]
+    assert string_view32_chars("Hi 🎂") == [72, 105, 32, 127874]
+
+    assert string_view_return() == "utf8 secret 🎂"
+    assert string_view16_return() == "utf16 secret 🎂"
+    assert string_view32_return() == "utf32 secret 🎂"
+
+    with capture:
+        string_view_print("Hi")
+        string_view_print("utf8 🎂")
+        string_view16_print("utf16 🎂")
+        string_view32_print("utf32 🎂")
+
+    assert capture == """
+        Hi 2
+        utf8 🎂 9
+        utf16 🎂 8
+        utf32 🎂 7
+    """
+
+    with capture:
+        string_view_print("Hi, ascii")
+        string_view_print("Hi, utf8 🎂")
+        string_view16_print("Hi, utf16 🎂")
+        string_view32_print("Hi, utf32 🎂")
+    assert capture == """
+        Hi, ascii 9
+        Hi, utf8 🎂 13
+        Hi, utf16 🎂 12
+        Hi, utf32 🎂 11
+    """
+
+
 def test_builtins_cast_return_none():
     """Casters produced with PYBIND11_TYPE_CASTER() should convert nullptr to None"""
     import pybind11_tests as m