Combine std::tuple/std::pair logic

The std::pair caster can be written as a special case of the std::tuple
caster; this combines them via a base `tuple_caster` class (which is
essentially identical to the previous std::tuple caster).

This also removes the special empty tuple base case: returning an empty
tuple is relatively rare, and the base case still works perfectly well
even when the tuple types is an empty list.
diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h
index 22eceb0..e814cab 100644
--- a/include/pybind11/cast.h
+++ b/include/pybind11/cast.h
@@ -1255,50 +1255,13 @@
     template <typename _T> using cast_op_type = remove_reference_t<pybind11::detail::cast_op_type<_T>>;
 };
 
-template <typename T1, typename T2> class type_caster<std::pair<T1, T2>> {
-    typedef std::pair<T1, T2> type;
-public:
-    bool load(handle src, bool convert) {
-        if (!isinstance<sequence>(src))
-            return false;
-        const auto seq = reinterpret_borrow<sequence>(src);
-        if (seq.size() != 2)
-            return false;
-        return first.load(seq[0], convert) && second.load(seq[1], convert);
-    }
-
-    static handle cast(const type &src, return_value_policy policy, handle parent) {
-        auto o1 = reinterpret_steal<object>(make_caster<T1>::cast(src.first, policy, parent));
-        auto o2 = reinterpret_steal<object>(make_caster<T2>::cast(src.second, policy, parent));
-        if (!o1 || !o2)
-            return handle();
-        tuple result(2);
-        PyTuple_SET_ITEM(result.ptr(), 0, o1.release().ptr());
-        PyTuple_SET_ITEM(result.ptr(), 1, o2.release().ptr());
-        return result.release();
-    }
-
-    static PYBIND11_DESCR name() {
-        return type_descr(
-            _("Tuple[") + make_caster<T1>::name() + _(", ") + make_caster<T2>::name() + _("]")
-        );
-    }
-
-    template <typename T> using cast_op_type = type;
-
-    operator type() & { return type(cast_op<T1>(first), cast_op<T2>(second)); }
-    operator type() && { return type(cast_op<T1>(std::move(first)), cast_op<T2>(std::move(second))); }
-protected:
-    make_caster<T1> first;
-    make_caster<T2> second;
-};
-
-template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
-    using type = std::tuple<Tuple...>;
-    using indices = make_index_sequence<sizeof...(Tuple)>;
+// Base implementation for std::tuple and std::pair
+template <template<typename...> class TupleType, typename... Tuple> class tuple_caster {
+    using type = TupleType<Tuple...>;
     static constexpr auto size = sizeof...(Tuple);
-
+    using indices = make_index_sequence<size>;
 public:
+
     bool load(handle src, bool convert) {
         if (!isinstance<sequence>(src))
             return false;
@@ -1327,7 +1290,6 @@
     template <size_t... Is>
     type implicit_cast(index_sequence<Is...>) && { return type(cast_op<Tuple>(std::move(std::get<Is>(subcasters)))...); }
 
-
     static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; }
 
     template <size_t... Is>
@@ -1338,9 +1300,6 @@
         return true;
     }
 
-    static handle cast_impl(const type &, return_value_policy, handle,
-                            index_sequence<>) { return tuple().release(); }
-
     /* Implementation: Convert a C++ tuple into a Python tuple */
     template <size_t... Is>
     static handle cast_impl(const type &src, return_value_policy policy, handle parent, index_sequence<Is...>) {
@@ -1357,9 +1316,15 @@
         return result.release();
     }
 
-    std::tuple<make_caster<Tuple>...> subcasters;
+    TupleType<make_caster<Tuple>...> subcasters;
 };
 
+template <typename T1, typename T2> class type_caster<std::pair<T1, T2>>
+    : public tuple_caster<std::pair, T1, T2> {};
+
+template <typename... Tuple> class type_caster<std::tuple<Tuple...>>
+    : public tuple_caster<std::tuple, Tuple...> {};
+
 /// Helper class which abstracts away certain actions. Users can provide specializations for
 /// custom holders, but it's only necessary if the type has a non-standard interface.
 template <typename T>
diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp
index 55269ba..6733402 100644
--- a/tests/test_builtin_casters.cpp
+++ b/tests/test_builtin_casters.cpp
@@ -85,7 +85,7 @@
     m.def("tuple_passthrough", [](std::tuple<bool, std::string, int> input) {
         return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
     }, "Return a triple in reversed order");
-
+    m.def("empty_tuple", []() { return std::tuple<>(); });
 
     // test_builtins_cast_return_none
     m.def("return_none_string", []() -> std::string * { return nullptr; });
diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py
index a6f9b57..32eba45 100644
--- a/tests/test_builtin_casters.py
+++ b/tests/test_builtin_casters.py
@@ -188,6 +188,7 @@
     # Any sequence can be cast to a std::pair or std::tuple
     assert m.pair_passthrough([True, "test"]) == ("test", True)
     assert m.tuple_passthrough([True, "test", 5]) == (5, "test", True)
+    assert m.empty_tuple() == ()
 
     assert doc(m.pair_passthrough) == """
         pair_passthrough(arg0: Tuple[bool, str]) -> Tuple[str, bool]