Adding bind_map
diff --git a/docs/advanced.rst b/docs/advanced.rst
index 3018600..979e0bf 100644
--- a/docs/advanced.rst
+++ b/docs/advanced.rst
@@ -551,6 +551,18 @@
 enabled. The types ``std::pair<>`` and ``std::tuple<>`` are already supported
 out of the box with just the core :file:`pybind11/pybind11.h` header.
 
+Alternatively it might be desirable to bind STL containers as native C++ classes,
+eliminating the need of converting back and forth between C++ representation
+and Python one. The downside of this approach in this case users will have to
+deal with C++ containers directly instead of using already familiar Python lists
+or dicts.
+
+Pybind11 provide set of binder functions to bind various STL containers like vectors,
+maps etc. All binder functions are designed to return instances of pybind11::class_
+objects so developers can bind extra functions if needed. For complete set of
+available functions please see :file:`pybind11/stl_bind.h`. For an example on using
+this feature, please see :file:`tests/test_stl_binders.cpp`.
+
 .. note::
 
     Arbitrary nesting of any of these types is supported.
diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h
index 9434510..2612b51 100644
--- a/include/pybind11/stl_bind.h
+++ b/include/pybind11/stl_bind.h
@@ -12,6 +12,7 @@
 #include "common.h"
 #include "operators.h"
 
+#include <map>
 #include <type_traits>
 #include <utility>
 #include <algorithm>
@@ -130,10 +131,12 @@
 
 NAMESPACE_END(detail)
 
-
-template <typename T, typename Allocator = std::allocator<T>, typename holder_type = std::unique_ptr<std::vector<T, Allocator>>, typename... Args>
-pybind11::class_<std::vector<T, Allocator>, holder_type> bind_vector(pybind11::module &m, std::string const &name, Args&&... args) {
-    using Vector = std::vector<T, Allocator>;
+//
+// std::vector
+//
+template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
+pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::string const &name, Args&&... args) {
+	using T = typename Vector::value_type;
     using SizeType = typename Vector::size_type;
     using DiffType = typename Vector::difference_type;
     using ItType   = typename Vector::iterator;
@@ -350,4 +353,118 @@
     return cl;
 }
 
+
+
+//
+// std::map
+//
+
+NAMESPACE_BEGIN(detail)
+
+/* Fallback functions */
+template <typename, typename, typename... Args> void map_if_insertion_operator(const Args&...) { }
+
+template <typename Map, typename Class_, typename... Args> void map_if_copy_assignable(Class_ &cl, const Args&...) {
+    using KeyType = typename Map::key_type;
+    using MappedType = typename Map::mapped_type;
+
+    cl.def("__setitem__",
+           [](Map &m, const KeyType &k, const MappedType &v) {
+               auto it = m.find(k);
+               if (it != m.end()) it->second = v;
+               else m.emplace(k, v);
+           });
+
+}
+
+template<typename Map, typename Class_, typename std::enable_if<!std::is_copy_assignable<typename Map::mapped_type>::value, int>::type = 0>
+void map_if_copy_assignable(Class_ &cl) {
+    using KeyType = typename Map::key_type;
+    using MappedType = typename Map::mapped_type;
+
+    cl.def("__setitem__",
+           [](Map &m, const KeyType &k, const MappedType &v) {
+               auto r = m.insert( std::make_pair(k, v) ); // We can't use m[k] = v; because value type might not be default constructable
+               if (!r.second) { // value type might be const so the only way to insert it is to errase it first...
+                   m.erase(r.first);
+                   m.insert( std::make_pair(k, v) );
+               }
+           });
+}
+
+
+template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &cl, std::string const &name)
+-> decltype(std::declval<std::ostream&>() << std::declval<typename Map::key_type>() << std::declval<typename Map::mapped_type>(), void()) {
+
+    cl.def("__repr__",
+           [name](Map &m) {
+            std::ostringstream s;
+            s << name << '{';
+            bool f = false;
+            for (auto const & kv : m) {
+                if (f) s << ", ";
+                s << kv.first << ": " << kv.second;
+                f = true;
+            }
+            s << '}';
+            return s.str();
+        },
+        "Return the canonical string representation of this map."
+    );
+}
+NAMESPACE_END(detail)
+
+template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
+pybind11::class_<Map, holder_type> bind_map(module &m, const std::string &name, Args&&... args) {
+    using KeyType = typename Map::key_type;
+    using MappedType = typename Map::mapped_type;
+    using Class_ = pybind11::class_<Map, holder_type>;
+
+    Class_ cl(m, name.c_str(), std::forward<Args>(args)...);
+
+    cl.def(pybind11::init<>());
+
+    // Register stream insertion operator (if possible)
+    detail::map_if_insertion_operator<Map, Class_>(cl, name);
+
+    cl.def("__bool__",
+        [](const Map &m) -> bool {
+            return !m.empty();
+        },
+        "Check whether the map is nonempty"
+    );
+
+    cl.def("__iter__",
+        [](Map &m) {
+            return pybind11::make_key_iterator(m.begin(), m.end());
+        },
+        pybind11::keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
+    );
+
+    cl.def("items",
+           [](Map &m) { return pybind11::make_iterator(m.begin(), m.end()); },
+           pybind11::keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
+    );
+
+    cl.def("__getitem__",
+           [](Map &m, const KeyType &k) -> MappedType {
+               auto it = m.find(k);
+               if (it != m.end()) return it->second;
+               else throw pybind11::key_error();  // it is not always possible to convert key to string // pybind11::key_error(k)
+   });
+
+    detail::map_if_copy_assignable<Map, Class_>(cl);
+
+    cl.def("__delitem__",
+        [](Map &m, const KeyType &k) {
+               auto it = m.find(k);
+               if (it != m.end()) return m.erase(it);
+               else throw pybind11::key_error(); // it is not always possible to convert key to string // pybind11::key_error(k)
+    });
+
+    cl.def("__len__", &Map::size);
+
+    return cl;
+}
+
 NAMESPACE_END(pybind11)
diff --git a/tests/test_stl_binders.cpp b/tests/test_stl_binders.cpp
index a6ed6bf..dabcaf0 100644
--- a/tests/test_stl_binders.cpp
+++ b/tests/test_stl_binders.cpp
@@ -28,10 +28,18 @@
     py::class_<El>(m, "El")
         .def(py::init<int>());
 
-    py::bind_vector<unsigned int>(m, "VectorInt");
-    py::bind_vector<bool>(m, "VectorBool");
+    py::bind_vector< std::vector<unsigned int> >(m, "VectorInt");
+    py::bind_vector< std::vector<bool> >(m, "VectorBool");
 
-    py::bind_vector<El>(m, "VectorEl");
+    py::bind_vector< std::vector<El> >(m, "VectorEl");
 
-    py::bind_vector<std::vector<El>>(m, "VectorVectorEl");
+    py::bind_vector< std::vector< std::vector<El> > >(m, "VectorVectorEl");
+});
+
+test_initializer stl_binder_map([](py::module &m) {
+    py::bind_map< std::map<std::string, double> >(m, "MapStringDouble");
+    py::bind_map< std::unordered_map<std::string, double> >(m, "UnorderedMapStringDouble");
+
+    py::bind_map< std::map<std::string, double const> >(m, "MapStringDoubleConst");
+    py::bind_map< std::unordered_map<std::string, double const> >(m, "UnorderedMapStringDoubleConst");
 });
diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py
index 2aaaf3a..3026357 100644
--- a/tests/test_stl_binders.py
+++ b/tests/test_stl_binders.py
@@ -1,5 +1,3 @@
-
-
 def test_vector_int():
     from pybind11_tests import VectorInt
 
@@ -51,3 +49,51 @@
     for i in range(10):
         assert vv_c[i] == (i % 2 == 0)
     assert str(vv_c) == "VectorBool[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]"
+
+
+def test_map_string_double():
+    from pybind11_tests import MapStringDouble, UnorderedMapStringDouble
+
+    m = MapStringDouble()
+    m['a'] = 1
+    m['b'] = 2.5
+
+    keys = []
+    for k in m: keys.append(k)
+    assert keys == ['a', 'b']
+
+    key_values = []
+    for k, v in m.items(): key_values.append( (k, v) )
+    assert key_values == [('a', 1), ('b', 2.5) ]
+
+    assert str(m) == "MapStringDouble{a: 1, b: 2.5}"
+
+
+    um = UnorderedMapStringDouble()
+    um['ua'] = 1.1
+    um['ub'] = 2.6
+
+    keys = []
+    for k in um: keys.append(k)
+    assert sorted(keys) == ['ua', 'ub']
+
+    key_values = []
+    for k, v in um.items(): key_values.append( (k, v) )
+    assert sorted(key_values) == [('ua', 1.1), ('ub', 2.6) ]
+
+    str(um)
+
+
+def test_map_string_double_const():
+    from pybind11_tests import MapStringDoubleConst, UnorderedMapStringDoubleConst
+
+    mc = MapStringDoubleConst()
+    mc['a'] = 10
+    mc['b'] = 20.5
+    assert str(mc) == "MapStringDoubleConst{a: 10, b: 20.5}"
+
+    umc = UnorderedMapStringDoubleConst()
+    umc['a'] = 11
+    umc['b'] = 21.5
+
+    str(umc)