Adds automatic casting on assignment of non-pyobject types (#551)

This adds automatic casting when assigning to python types like dict,
list, and attributes.  Instead of:

    dict["key"] = py::cast(val);
    m.attr("foo") = py::cast(true);
    list.append(py::cast(42));

you can now simply write:

    dict["key"] = val;
    m.attr("foo") = true;
    list.append(42);

Casts needing extra parameters (e.g. for a non-default rvp) still
require the py::cast() call. set::add() is also supported.

All usage is channeled through a SFINAE implementation which either just returns or casts. 

Combined non-converting handle and autocasting template methods via a
helper method that either just returns (handle) or casts (C++ type).
diff --git a/docs/basics.rst b/docs/basics.rst
index 45272b7..c1dd474 100644
--- a/docs/basics.rst
+++ b/docs/basics.rst
@@ -254,16 +254,18 @@
 Exporting variables
 ===================
 
-To expose a value from C++, use the ``attr`` function to register it in a module
-as shown below. Built-in types and general objects (more on that later) can be
+To expose a value from C++, use the ``attr`` function to register it in a
+module as shown below. Built-in types and general objects (more on that later)
+are automatically converted when assigned as attributes, and can be explicitly
 converted using the function ``py::cast``.
 
 .. code-block:: cpp
 
     PYBIND11_PLUGIN(example) {
         py::module m("example", "pybind11 example plugin");
-        m.attr("the_answer") = py::cast(42);
-        m.attr("what") = py::cast("World");
+        m.attr("the_answer") = 42;
+        py::object world = py::cast("World");
+        m.attr("what") = world;
         return m.ptr();
     }
 
diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h
index 6cdc482..535516b 100644
--- a/include/pybind11/cast.h
+++ b/include/pybind11/cast.h
@@ -1120,6 +1120,10 @@
 
 NAMESPACE_BEGIN(detail)
 
+// Declared in pytypes.h:
+template <typename T, enable_if_t<!is_pyobject<T>::value, int>>
+object object_or_cast(T &&o) { return pybind11::cast(std::forward<T>(o)); }
+
 struct overload_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro
 template <typename ret_type> using overload_caster_t = conditional_t<
     cast_is_temporary_value_reference<ret_type>::value, make_caster<ret_type>, overload_unused>;
diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h
index d078d58..2b49ecf 100644
--- a/include/pybind11/pytypes.h
+++ b/include/pybind11/pytypes.h
@@ -43,7 +43,7 @@
 
 /// Tag and check to identify a class which implements the Python object API
 class pyobject_tag { };
-template <typename T> using is_pyobject = std::is_base_of<pyobject_tag, T>;
+template <typename T> using is_pyobject = std::is_base_of<pyobject_tag, typename std::remove_reference<T>::type>;
 
 /// Mixin which adds common functions to handle, object and various accessors.
 /// The only requirement for `Derived` is to implement `PyObject *Derived::ptr() const`.
@@ -81,7 +81,7 @@
 class handle : public detail::object_api<handle> {
 public:
     handle() = default;
-    handle(PyObject *ptr) : m_ptr(ptr) { }
+    handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject*
 
     PyObject *ptr() const { return m_ptr; }
     PyObject *&ptr() { return m_ptr; }
@@ -224,6 +224,18 @@
     return value;
 }
 
+// Helper aliases/functions to support implicit casting of values given to python accessors/methods.
+// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes
+// through pybind11::cast(obj) to convert it to an `object`.
+template <typename T, enable_if_t<is_pyobject<T>::value, int> = 0>
+auto object_or_cast(T &&o) -> decltype(std::forward<T>(o)) { return std::forward<T>(o); }
+// The following casting version is implemented in cast.h:
+template <typename T, enable_if_t<!is_pyobject<T>::value, int> = 0>
+object object_or_cast(T &&o);
+// Match a PyObject*, which we want to convert directly to handle via its converting constructor
+inline handle object_or_cast(PyObject *ptr) { return ptr; }
+
+
 template <typename Policy>
 class accessor : public object_api<accessor<Policy>> {
     using key_type = typename Policy::key_type;
@@ -231,12 +243,17 @@
 public:
     accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { }
 
+    // accessor overload required to override default assignment operator (templates are not allowed
+    // to replace default compiler-generated assignments).
     void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); }
     void operator=(const accessor &a) & { operator=(handle(a)); }
-    void operator=(const object &o) && { std::move(*this).operator=(handle(o)); }
-    void operator=(const object &o) & { operator=(handle(o)); }
-    void operator=(handle value) && { Policy::set(obj, key, value); }
-    void operator=(handle value) & { get_cache() = reinterpret_borrow<object>(value); }
+
+    template <typename T> void operator=(T &&value) && {
+        Policy::set(obj, key, object_or_cast(std::forward<T>(value)));
+    }
+    template <typename T> void operator=(T &&value) & {
+        get_cache() = reinterpret_borrow<object>(object_or_cast(std::forward<T>(value)));
+    }
 
     template <typename T = Policy>
     PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)")
@@ -773,7 +790,9 @@
     }
     size_t size() const { return (size_t) PyList_Size(m_ptr); }
     detail::list_accessor operator[](size_t index) const { return {*this, index}; }
-    void append(handle h) const { PyList_Append(m_ptr, h.ptr()); }
+    template <typename T> void append(T &&val) const {
+        PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
+    }
 };
 
 class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) };
@@ -786,7 +805,9 @@
         if (!m_ptr) pybind11_fail("Could not allocate set object!");
     }
     size_t size() const { return (size_t) PySet_Size(m_ptr); }
-    bool add(const object &object) const { return PySet_Add(m_ptr, object.ptr()) == 0; }
+    template <typename T> bool add(T &&val) const {
+        return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
+    }
     void clear() const { PySet_Clear(m_ptr); }
 };
 
diff --git a/tests/pybind11_tests.cpp b/tests/pybind11_tests.cpp
index 35981a0..9c593ee 100644
--- a/tests/pybind11_tests.cpp
+++ b/tests/pybind11_tests.cpp
@@ -39,7 +39,7 @@
     for (const auto &initializer : initializers())
         initializer(m);
 
-    if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = py::cast(false);
+    if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = false;
 
     return m.ptr();
 }
diff --git a/tests/test_eigen.cpp b/tests/test_eigen.cpp
index a9cb9f2..588cdce 100644
--- a/tests/test_eigen.cpp
+++ b/tests/test_eigen.cpp
@@ -40,7 +40,7 @@
     typedef Eigen::SparseMatrix<float, Eigen::RowMajor> SparseMatrixR;
     typedef Eigen::SparseMatrix<float> SparseMatrixC;
 
-    m.attr("have_eigen") = py::cast(true);
+    m.attr("have_eigen") = true;
 
     // Non-symmetric matrix with zero elements
     Eigen::MatrixXf mat(5, 6);
diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp
index ca2afa6..706b500 100644
--- a/tests/test_exceptions.cpp
+++ b/tests/test_exceptions.cpp
@@ -88,7 +88,7 @@
 
 struct PythonCallInDestructor {
     PythonCallInDestructor(const py::dict &d) : d(d) {}
-    ~PythonCallInDestructor() { d["good"] = py::cast(true); }
+    ~PythonCallInDestructor() { d["good"] = true; }
 
     py::dict d;
 };
diff --git a/tests/test_issues.cpp b/tests/test_issues.cpp
index 378da52..4c59a1b 100644
--- a/tests/test_issues.cpp
+++ b/tests/test_issues.cpp
@@ -381,6 +381,11 @@
         .def_static("make", &MyDerived::make)
         .def_static("make2", &MyDerived::make);
 
+    py::dict d;
+    std::string bar = "bar";
+    d["str"] = bar;
+    d["num"] = 3.7;
+
     /// Issue #528: templated constructor
     m2.def("tpl_constr_vector", [](std::vector<TplConstrClass> &) {});
     m2.def("tpl_constr_map", [](std::unordered_map<TplConstrClass, TplConstrClass> &) {});
diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp
index 68e07ad..33c655b 100644
--- a/tests/test_python_types.cpp
+++ b/tests/test_python_types.cpp
@@ -37,7 +37,8 @@
     py::set get_set() {
         py::set set;
         set.add(py::str("key1"));
-        set.add(py::str("key2"));
+        set.add("key2");
+        set.add(std::string("key3"));
         return set;
     }
 
@@ -59,7 +60,7 @@
     /* Create, manipulate, and return a Python list */
     py::list get_list() {
         py::list list;
-        list.append(py::str("value"));
+        list.append("value");
         py::print("Entry at position 0:", list[0]);
         list[0] = py::str("overwritten");
         return list;
@@ -269,7 +270,7 @@
             d["missing_attr_chain"] = "raised"_s;
         }
 
-        d["is_none"] = py::cast(o.attr("basic_attr").is_none());
+        d["is_none"] = o.attr("basic_attr").is_none();
 
         d["operator()"] = o.attr("func")(1);
         d["operator*"] = o.attr("func")(*o.attr("begin_end"));
@@ -279,13 +280,13 @@
 
     m.def("test_tuple_accessor", [](py::tuple existing_t) {
         try {
-            existing_t[0] = py::cast(1);
+            existing_t[0] = 1;
         } catch (const py::error_already_set &) {
             // --> Python system error
             // Only new tuples (refcount == 1) are mutable
             auto new_t = py::tuple(3);
             for (size_t i = 0; i < new_t.size(); ++i) {
-                new_t[i] = py::cast(i);
+                new_t[i] = i;
             }
             return new_t;
         }
@@ -294,15 +295,15 @@
 
     m.def("test_accessor_assignment", []() {
         auto l = py::list(1);
-        l[0] = py::cast(0);
+        l[0] = 0;
 
         auto d = py::dict();
         d["get"] = l[0];
         auto var = l[0];
         d["deferred_get"] = var;
-        l[0] = py::cast(1);
+        l[0] = 1;
         d["set"] = l[0];
-        var = py::cast(99); // this assignment should not overwrite l[0]
+        var = 99; // this assignment should not overwrite l[0]
         d["deferred_set"] = l[0];
         d["var"] = var;
 
@@ -338,8 +339,8 @@
     }, py::arg_v("x", std::experimental::nullopt, "None"));
 #endif
 
-    m.attr("has_optional") = py::cast(has_optional);
-    m.attr("has_exp_optional") = py::cast(has_exp_optional);
+    m.attr("has_optional") = has_optional;
+    m.attr("has_exp_optional") = has_exp_optional;
 
     m.def("test_default_constructors", []() {
         return py::dict(
@@ -389,4 +390,41 @@
     py::class_<MoveOutContainer>(m, "MoveOutContainer")
         .def(py::init<>())
         .def_property_readonly("move_list", &MoveOutContainer::move_list);
+
+    m.def("get_implicit_casting", []() {
+        py::dict d;
+        d["char*_i1"] = "abc";
+        const char *c2 = "abc";
+        d["char*_i2"] = c2;
+        d["char*_e"] = py::cast(c2);
+        d["char*_p"] = py::str(c2);
+
+        d["int_i1"] = 42;
+        int i = 42;
+        d["int_i2"] = i;
+        i++;
+        d["int_e"] = py::cast(i);
+        i++;
+        d["int_p"] = py::int_(i);
+
+        d["str_i1"] = std::string("str");
+        std::string s2("str1");
+        d["str_i2"] = s2;
+        s2[3] = '2';
+        d["str_e"] = py::cast(s2);
+        s2[3] = '3';
+        d["str_p"] = py::str(s2);
+
+        py::list l(2);
+        l[0] = 3;
+        l[1] = py::cast(6);
+        l.append(9);
+        l.append(py::cast(12));
+        l.append(py::int_(15));
+
+        return py::dict(
+            "d"_a=d,
+            "l"_a=l
+        );
+    });
 });
diff --git a/tests/test_python_types.py b/tests/test_python_types.py
index 50a707e..b90b44c 100644
--- a/tests/test_python_types.py
+++ b/tests/test_python_types.py
@@ -38,12 +38,13 @@
     """
     with capture:
         set_result = instance.get_set()
-        set_result.add('key3')
+        set_result.add('key4')
         instance.print_set(set_result)
     assert capture.unordered == """
         key: key1
         key: key2
         key: key3
+        key: key4
     """
     with capture:
         set_result = instance.get_set2()
@@ -386,3 +387,16 @@
     c = MoveOutContainer()
     moved_out_list = c.move_list
     assert [x.value for x in moved_out_list] == [0, 1, 2]
+
+
+def test_implicit_casting():
+    """Tests implicit casting when assigning or appending to dicts and lists."""
+    from pybind11_tests import get_implicit_casting
+
+    z = get_implicit_casting()
+    assert z['d'] == {
+        'char*_i1': 'abc', 'char*_i2': 'abc', 'char*_e': 'abc', 'char*_p': 'abc',
+        'str_i1': 'str', 'str_i2': 'str1', 'str_e': 'str2', 'str_p': 'str3',
+        'int_i1': 42, 'int_i2': 42, 'int_e': 43, 'int_p': 44
+    }
+    assert z['l'] == [3, 6, 9, 12, 15]