diff --git a/doc/mp11/map.adoc b/doc/mp11/map.adoc
index ac7d59d..3358eb4 100644
--- a/doc/mp11/map.adoc
+++ b/doc/mp11/map.adoc
@@ -15,6 +15,12 @@ http://www.boost.org/LICENSE_1_0.txt
A map is a list of lists, the inner lists having at least one element (the key.) The keys of the map must be unique.
+## mp_is_map
+
+ template using mp_is_map = /*...*/;
+
+`mp_is_map` is `mp_true` if `M` is a map, `mp_false` otherwise.
+
## mp_map_find
template using mp_map_find = /*...*/;
diff --git a/include/boost/mp11/map.hpp b/include/boost/mp11/map.hpp
index 85e233a..b1ced45 100644
--- a/include/boost/mp11/map.hpp
+++ b/include/boost/mp11/map.hpp
@@ -13,6 +13,8 @@
#include
#include
#include
+#include
+#include
#include
namespace boost
@@ -82,6 +84,34 @@ template using mp_map_erase = typename detail::mp_map_erase_im
// mp_map_keys
template using mp_map_keys = mp_transform;
+// mp_is_map
+namespace detail
+{
+
+template struct mp_is_map_element: mp_false
+{
+};
+
+template class L, class T1, class... T> struct mp_is_map_element>: mp_true
+{
+};
+
+template using mp_keys_are_set = mp_is_set>;
+
+template struct mp_is_map_impl
+{
+ using type = mp_false;
+};
+
+template class M, class... T> struct mp_is_map_impl>
+{
+ using type = mp_eval_if...>>, mp_false, mp_keys_are_set, M>;
+};
+
+} // namespace detail
+
+template using mp_is_map = typename detail::mp_is_map_impl::type;
+
} // namespace mp11
} // namespace boost
diff --git a/test/Jamfile b/test/Jamfile
index 308036b..210a3da 100644
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -124,6 +124,7 @@ run mp_map_replace.cpp : : : $(REQ) ;
run mp_map_erase.cpp : : : $(REQ) ;
run mp_map_update.cpp : : : $(REQ) ;
run mp_map_keys.cpp : : : $(REQ) ;
+run mp_is_map.cpp : : : $(REQ) ;
# bind
run mp_bind.cpp : : : $(REQ) ;
diff --git a/test/mp_is_map.cpp b/test/mp_is_map.cpp
new file mode 100644
index 0000000..980d140
--- /dev/null
+++ b/test/mp_is_map.cpp
@@ -0,0 +1,59 @@
+
+// Copyright 2017 Peter Dimov.
+//
+// Distributed under the Boost Software License, Version 1.0.
+//
+// See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+int main()
+{
+ using boost::mp11::mp_is_map;
+ using boost::mp11::mp_list;
+ using boost::mp11::mp_true;
+ using boost::mp11::mp_false;
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same, mp_false>));
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same>, mp_true>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same>, mp_true>));
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same>, mp_false>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same>, mp_false>));
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same>>, mp_false>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same>>, mp_false>));
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same>>, mp_true>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same>>, mp_true>));
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same>>, mp_true>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same>>, mp_true>));
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same, std::pair>>, mp_true>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same, std::pair>>, mp_true>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same, std::pair>>, mp_true>));
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same, std::pair>>, mp_false>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same, std::pair>>, mp_false>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same, std::pair>>, mp_false>));
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same, mp_list, mp_list>>, mp_true>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same, mp_list, mp_list>>, mp_true>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same, std::pair, std::tuple>>, mp_true>));
+
+ BOOST_TEST_TRAIT_TRUE((std::is_same, mp_list, mp_list>>, mp_false>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same, mp_list, mp_list>>, mp_false>));
+ BOOST_TEST_TRAIT_TRUE((std::is_same, std::pair, std::tuple>>, mp_false>));
+
+ return boost::report_errors();
+}