Add type caster for std::variant and other variant-like classes
diff --git a/docs/advanced/cast/overview.rst b/docs/advanced/cast/overview.rst
index 54c11a9..49781dc 100644
--- a/docs/advanced/cast/overview.rst
+++ b/docs/advanced/cast/overview.rst
@@ -144,6 +144,8 @@
 +------------------------------------+---------------------------+-------------------------------+
 | ``std::experimental::optional<T>`` | STL optional type (exp.)  | :file:`pybind11/stl.h`        |
 +------------------------------------+---------------------------+-------------------------------+
+| ``std::variant<...>``              | Type-safe union (C++17)   | :file:`pybind11/stl.h`        |
++------------------------------------+---------------------------+-------------------------------+
 | ``std::function<...>``             | STL polymorphic function  | :file:`pybind11/functional.h` |
 +------------------------------------+---------------------------+-------------------------------+
 | ``std::chrono::duration<...>``     | STL time duration         | :file:`pybind11/chrono.h`     |
diff --git a/docs/advanced/cast/stl.rst b/docs/advanced/cast/stl.rst
index c76da5c..47c2a96 100644
--- a/docs/advanced/cast/stl.rst
+++ b/docs/advanced/cast/stl.rst
@@ -26,6 +26,51 @@
     The file :file:`tests/test_python_types.cpp` contains a complete
     example that demonstrates how to pass STL data types in more detail.
 
+C++17 library containers
+========================
+
+The :file:`pybind11/stl.h` header also includes support for ``std::optional<>``
+and ``std::variant<>``. These require a C++17 compiler and standard library.
+In C++14 mode, ``std::experimental::optional<>`` is supported if available.
+
+Various versions of these containers also exist for C++11 (e.g. in Boost).
+pybind11 provides an easy way to specialize the ``type_caster`` for such
+types:
+
+.. code-block:: cpp
+
+    // `boost::optional` as an example -- can be any `std::optional`-like container
+    namespace pybind11 { namespace detail {
+        template <typename T>
+        struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {};
+    }}
+
+The above should be placed in a header file and included in all translation units
+where automatic conversion is needed. Similarly, a specialization can be provided
+for custom variant types:
+
+.. code-block:: cpp
+
+    // `boost::variant` as an example -- can be any `std::variant`-like container
+    namespace pybind11 { namespace detail {
+        template <typename... Ts>
+        struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};
+
+        // Specifies the function used to visit the variant -- `apply_visitor` instead of `visit`
+        template <>
+        struct visit_helper<boost::variant> {
+            template <typename... Args>
+            static auto call(Args &&...args)
+                -> decltype(boost::apply_visitor(std::forward<Args>(args)...)) {
+                return boost::apply_visitor(std::forward<Args>(args)...);
+            }
+        };
+    }} // namespace pybind11::detail
+
+The ``visit_helper`` specialization is not required if your ``name::variant`` provides
+a ``name::visit()`` function. For any other function name, the specialization must be
+included to tell pybind11 how to visit the variant.
+
 .. _opaque:
 
 Making opaque types
diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h
index 4b557bd..329ff64 100644
--- a/include/pybind11/stl.h
+++ b/include/pybind11/stl.h
@@ -36,6 +36,11 @@
 #      define PYBIND11_HAS_EXP_OPTIONAL 1
 #    endif
 #  endif
+// std::variant
+#  if defined(PYBIND11_CPP17) && __has_include(<variant>)
+#    include <variant>
+#    define PYBIND11_HAS_VARIANT 1
+#  endif
 #endif
 
 NAMESPACE_BEGIN(pybind11)
@@ -262,6 +267,66 @@
     : public void_caster<std::experimental::nullopt_t> {};
 #endif
 
+/// Visit a variant and cast any found type to Python
+struct variant_caster_visitor {
+    return_value_policy policy;
+    handle parent;
+
+    template <typename T>
+    handle operator()(T &&src) const {
+        return make_caster<T>::cast(std::forward<T>(src), policy, parent);
+    }
+};
+
+/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar
+/// `namespace::variant` types which provide a `namespace::visit()` function are handled here
+/// automatically using argument-dependent lookup. Users can provide specializations for other
+/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`.
+template <template<typename...> class Variant>
+struct visit_helper {
+    template <typename... Args>
+    static auto call(Args &&...args) -> decltype(visit(std::forward<Args>(args)...)) {
+        return visit(std::forward<Args>(args)...);
+    }
+};
+
+/// Generic variant caster
+template <typename Variant> struct variant_caster;
+
+template <template<typename...> class V, typename... Ts>
+struct variant_caster<V<Ts...>> {
+    static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative.");
+
+    template <typename U, typename... Us>
+    bool load_alternative(handle src, bool convert, type_list<U, Us...>) {
+        auto caster = make_caster<U>();
+        if (caster.load(src, convert)) {
+            value = cast_op<U>(caster);
+            return true;
+        }
+        return load_alternative(src, convert, type_list<Us...>{});
+    }
+
+    bool load_alternative(handle, bool, type_list<>) { return false; }
+
+    bool load(handle src, bool convert) {
+        return load_alternative(src, convert, type_list<Ts...>{});
+    }
+
+    template <typename Variant>
+    static handle cast(Variant &&src, return_value_policy policy, handle parent) {
+        return visit_helper<V>::call(variant_caster_visitor{policy, parent},
+                                     std::forward<Variant>(src));
+    }
+
+    using Type = V<Ts...>;
+    PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster<Ts>::name()...) + _("]"));
+};
+
+#if PYBIND11_HAS_VARIANT
+template <typename... Ts>
+struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> { };
+#endif
 NAMESPACE_END(detail)
 
 inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp
index 18aa87a..776c4ce 100644
--- a/tests/test_python_types.cpp
+++ b/tests/test_python_types.cpp
@@ -354,6 +354,23 @@
     m.attr("has_optional") = has_optional;
     m.attr("has_exp_optional") = has_exp_optional;
 
+#ifdef PYBIND11_HAS_VARIANT
+    struct visitor {
+        const char *operator()(int) { return "int"; }
+        const char *operator()(std::string) { return "std::string"; }
+        const char *operator()(double) { return "double"; }
+    };
+
+    m.def("load_variant", [](std::variant<int, std::string, double> v) {
+        return std::visit(visitor(), v);
+    });
+
+    m.def("cast_variant", []() {
+        using V = std::variant<int, std::string>;
+        return py::make_tuple(V(5), V("Hello"));
+    });
+#endif
+
     m.def("test_default_constructors", []() {
         return py::dict(
             "str"_a=py::str(),
diff --git a/tests/test_python_types.py b/tests/test_python_types.py
index 9849bc8..45cf3e8 100644
--- a/tests/test_python_types.py
+++ b/tests/test_python_types.py
@@ -1,5 +1,6 @@
 # Python < 3 needs this: coding=utf-8
 import pytest
+import pybind11_tests
 
 from pybind11_tests import ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional
 
@@ -370,6 +371,18 @@
     assert test_nullopt_exp(43) == 43
 
 
+@pytest.mark.skipif(not hasattr(pybind11_tests, "load_variant"), reason='no <variant>')
+def test_variant(doc):
+    from pybind11_tests import load_variant, cast_variant
+
+    assert load_variant(1) == "int"
+    assert load_variant("1") == "std::string"
+    assert load_variant(1.0) == "double"
+    assert cast_variant() == (5, "Hello")
+
+    assert doc(load_variant) == "load_variant(arg0: Union[int, str, float]) -> str"
+
+
 def test_constructors():
     """C++ default and converting constructors are equivalent to type calls in Python"""
     from pybind11_tests import (test_default_constructors, test_converting_constructors,