diff --git a/include/nlohmann/detail/meta.hpp b/include/nlohmann/detail/meta.hpp
index 49f1069f..b251afb6 100644
--- a/include/nlohmann/detail/meta.hpp
+++ b/include/nlohmann/detail/meta.hpp
@@ -233,7 +233,7 @@ struct is_compatible_complete_type
 {
     static constexpr bool value =
         not std::is_base_of<std::istream, CompatibleCompleteType>::value and
-        not std::is_same<BasicJsonType, CompatibleCompleteType>::value and
+        not is_basic_json<CompatibleCompleteType>::value and
         not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and
         has_to_json<BasicJsonType, CompatibleCompleteType>::value;
 };
diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp
index 10183262..1b15bdaf 100644
--- a/include/nlohmann/detail/output/binary_writer.hpp
+++ b/include/nlohmann/detail/output/binary_writer.hpp
@@ -704,116 +704,126 @@ class binary_writer
         oa->write_characters(vec.data(), sizeof(NumberType));
     }
 
-    template<typename NumberType>
+    // UBJSON: write number (floating point)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_floating_point<NumberType>::value, int>::type = 0>
     void write_number_with_ubjson_prefix(const NumberType n,
                                          const bool add_prefix)
     {
-        if (std::is_floating_point<NumberType>::value)
+        if (add_prefix)
+        {
+            oa->write_character(static_cast<CharType>('D'));  // float64
+        }
+        write_number(n);
+    }
+
+    // UBJSON: write number (unsigned integer)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_unsigned<NumberType>::value, int>::type = 0>
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix)
+    {
+        if (n <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
         {
             if (add_prefix)
             {
-                oa->write_character(static_cast<CharType>('D'));  // float64
+                oa->write_character(static_cast<CharType>('i'));  // int8
             }
-            write_number(n);
+            write_number(static_cast<uint8_t>(n));
         }
-        else if (std::is_unsigned<NumberType>::value)
+        else if (n <= (std::numeric_limits<uint8_t>::max)())
         {
-            if (n <= (std::numeric_limits<int8_t>::max)())
+            if (add_prefix)
             {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('i'));  // int8
-                }
-                write_number(static_cast<uint8_t>(n));
+                oa->write_character(static_cast<CharType>('U'));  // uint8
             }
-            else if (n <= (std::numeric_limits<uint8_t>::max)())
+            write_number(static_cast<uint8_t>(n));
+        }
+        else if (n <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
+        {
+            if (add_prefix)
             {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('U'));  // uint8
-                }
-                write_number(static_cast<uint8_t>(n));
+                oa->write_character(static_cast<CharType>('I'));  // int16
             }
-            else if (n <= (std::numeric_limits<int16_t>::max)())
+            write_number(static_cast<int16_t>(n));
+        }
+        else if (n <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
+        {
+            if (add_prefix)
             {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('I'));  // int16
-                }
-                write_number(static_cast<int16_t>(n));
+                oa->write_character(static_cast<CharType>('l'));  // int32
             }
-            else if (n <= (std::numeric_limits<int32_t>::max)())
+            write_number(static_cast<int32_t>(n));
+        }
+        else if (n <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
+        {
+            if (add_prefix)
             {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('l'));  // int32
-                }
-                write_number(static_cast<int32_t>(n));
-            }
-            else if (n <= (std::numeric_limits<int64_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('L'));  // int64
-                }
-                write_number(static_cast<int64_t>(n));
-            }
-            else
-            {
-                JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
+                oa->write_character(static_cast<CharType>('L'));  // int64
             }
+            write_number(static_cast<int64_t>(n));
         }
         else
         {
-            if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('i'));  // int8
-                }
-                write_number(static_cast<int8_t>(n));
-            }
-            else if ((std::numeric_limits<uint8_t>::min)() <= n and n <= (std::numeric_limits<uint8_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('U'));  // uint8
-                }
-                write_number(static_cast<uint8_t>(n));
-            }
-            else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('I'));  // int16
-                }
-                write_number(static_cast<int16_t>(n));
-            }
-            else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('l'));  // int32
-                }
-                write_number(static_cast<int32_t>(n));
-            }
-            else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('L'));  // int64
-                }
-                write_number(static_cast<int64_t>(n));
-            }
-            // LCOV_EXCL_START
-            else
-            {
-                JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
-            }
-            // LCOV_EXCL_STOP
+            JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
         }
     }
 
+    // UBJSON: write number (signed integer)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_signed<NumberType>::value and
+                 not std::is_floating_point<NumberType>::value, int>::type = 0>
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix)
+    {
+        if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('i'));  // int8
+            }
+            write_number(static_cast<int8_t>(n));
+        }
+        else if (static_cast<int64_t>((std::numeric_limits<uint8_t>::min)()) <= n and n <= static_cast<int64_t>((std::numeric_limits<uint8_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('U'));  // uint8
+            }
+            write_number(static_cast<uint8_t>(n));
+        }
+        else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('I'));  // int16
+            }
+            write_number(static_cast<int16_t>(n));
+        }
+        else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('l'));  // int32
+            }
+            write_number(static_cast<int32_t>(n));
+        }
+        else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('L'));  // int64
+            }
+            write_number(static_cast<int64_t>(n));
+        }
+        // LCOV_EXCL_START
+        else
+        {
+            JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
+        }
+        // LCOV_EXCL_STOP
+    }
+
     /*!
     @brief determine the type prefix of container values
 
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index cef504ff..3a52ec8c 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -1210,6 +1210,7 @@ class basic_json
     - @a CompatibleType is not derived from `std::istream`,
     - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
          constructors),
+    - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)
     - @a CompatibleType is not a @ref basic_json nested type (e.g.,
          @ref json_pointer, @ref iterator, etc ...)
     - @ref @ref json_serializer<U> has a
@@ -1245,6 +1246,78 @@ class basic_json
         assert_invariant();
     }
 
+    /*!
+    @brief create a JSON value from an existing one
+
+    This is a constructor for existing @ref basic_json types.
+    It does not hijack copy/move constructors, since the parameter has different
+    template arguments than the current ones.
+
+    The constructor tries to convert the internal @ref m_value of the parameter.
+
+    @tparam BasicJsonType a type such that:
+    - @a BasicJsonType is a @ref basic_json type.
+    - @a BasicJsonType has different template arguments than @ref basic_json_t.
+
+    @param[in] val the @ref basic_json value to be converted.
+
+    @complexity Usually linear in the size of the passed @a val, also
+                depending on the implementation of the called `to_json()`
+                method.
+
+    @exceptionsafety Depends on the called constructor. For types directly
+    supported by the library (i.e., all types for which no `to_json()` function
+    was provided), strong guarantee holds: if an exception is thrown, there are
+    no changes to any JSON value.
+
+    @since version 3.1.2
+    */
+    template <typename BasicJsonType,
+              detail::enable_if_t<
+                  detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0>
+    basic_json(const BasicJsonType& val)
+    {
+        using other_boolean_t = typename BasicJsonType::boolean_t;
+        using other_number_float_t = typename BasicJsonType::number_float_t;
+        using other_number_integer_t = typename BasicJsonType::number_integer_t;
+        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+        using other_string_t = typename BasicJsonType::string_t;
+        using other_object_t = typename BasicJsonType::object_t;
+        using other_array_t = typename BasicJsonType::array_t;
+
+        switch (val.type())
+        {
+            case value_t::boolean:
+                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
+                break;
+            case value_t::number_float:
+                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
+                break;
+            case value_t::number_integer:
+                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
+                break;
+            case value_t::number_unsigned:
+                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
+                break;
+            case value_t::string:
+                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
+                break;
+            case value_t::object:
+                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
+                break;
+            case value_t::array:
+                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
+                break;
+            case value_t::null:
+                *this = nullptr;
+                break;
+            case value_t::discarded:
+                m_type = value_t::discarded;
+                break;
+        }
+        assert_invariant();
+    }
+
     /*!
     @brief create a container (array or object) from an initializer list
 
@@ -2417,6 +2490,29 @@ class basic_json
         return *this;
     }
 
+    /*!
+    @brief get special-case overload
+
+    This overloads converts the current @ref basic_json in a different
+    @ref basic_json type
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this, converted into @tparam BasicJsonType
+
+    @complexity Depending on the implementation of the called `from_json()`
+                method.
+
+    @since version 3.1.2
+    */
+    template<typename BasicJsonType, detail::enable_if_t<
+                 not std::is_same<BasicJsonType, basic_json>::value and
+                 detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    BasicJsonType get() const
+    {
+        return *this;
+    }
+
     /*!
     @brief get a value (explicit)
 
@@ -2458,7 +2554,7 @@ class basic_json
     */
     template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
              detail::enable_if_t <
-                 not std::is_same<basic_json_t, ValueType>::value and
+                 not detail::is_basic_json<ValueType>::value and
                  detail::has_from_json<basic_json_t, ValueType>::value and
                  not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
                  int> = 0>
@@ -2724,7 +2820,8 @@ class basic_json
     template < typename ValueType, typename std::enable_if <
                    not std::is_pointer<ValueType>::value and
                    not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
-                   not std::is_same<ValueType, typename string_t::value_type>::value
+                   not std::is_same<ValueType, typename string_t::value_type>::value and
+                   not detail::is_basic_json<ValueType>::value
 #ifndef _MSC_VER  // fix for issue #167 operator<< ambiguity under VS2015
                    and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
 #endif
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 8aaa1f68..9fc301e2 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -466,7 +466,7 @@ struct is_compatible_complete_type
 {
     static constexpr bool value =
         not std::is_base_of<std::istream, CompatibleCompleteType>::value and
-        not std::is_same<BasicJsonType, CompatibleCompleteType>::value and
+        not is_basic_json<CompatibleCompleteType>::value and
         not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and
         has_to_json<BasicJsonType, CompatibleCompleteType>::value;
 };
@@ -7401,116 +7401,126 @@ class binary_writer
         oa->write_characters(vec.data(), sizeof(NumberType));
     }
 
-    template<typename NumberType>
+    // UBJSON: write number (floating point)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_floating_point<NumberType>::value, int>::type = 0>
     void write_number_with_ubjson_prefix(const NumberType n,
                                          const bool add_prefix)
     {
-        if (std::is_floating_point<NumberType>::value)
+        if (add_prefix)
+        {
+            oa->write_character(static_cast<CharType>('D'));  // float64
+        }
+        write_number(n);
+    }
+
+    // UBJSON: write number (unsigned integer)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_unsigned<NumberType>::value, int>::type = 0>
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix)
+    {
+        if (n <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
         {
             if (add_prefix)
             {
-                oa->write_character(static_cast<CharType>('D'));  // float64
+                oa->write_character(static_cast<CharType>('i'));  // int8
             }
-            write_number(n);
+            write_number(static_cast<uint8_t>(n));
         }
-        else if (std::is_unsigned<NumberType>::value)
+        else if (n <= (std::numeric_limits<uint8_t>::max)())
         {
-            if (n <= (std::numeric_limits<int8_t>::max)())
+            if (add_prefix)
             {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('i'));  // int8
-                }
-                write_number(static_cast<uint8_t>(n));
+                oa->write_character(static_cast<CharType>('U'));  // uint8
             }
-            else if (n <= (std::numeric_limits<uint8_t>::max)())
+            write_number(static_cast<uint8_t>(n));
+        }
+        else if (n <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
+        {
+            if (add_prefix)
             {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('U'));  // uint8
-                }
-                write_number(static_cast<uint8_t>(n));
+                oa->write_character(static_cast<CharType>('I'));  // int16
             }
-            else if (n <= (std::numeric_limits<int16_t>::max)())
+            write_number(static_cast<int16_t>(n));
+        }
+        else if (n <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
+        {
+            if (add_prefix)
             {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('I'));  // int16
-                }
-                write_number(static_cast<int16_t>(n));
+                oa->write_character(static_cast<CharType>('l'));  // int32
             }
-            else if (n <= (std::numeric_limits<int32_t>::max)())
+            write_number(static_cast<int32_t>(n));
+        }
+        else if (n <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
+        {
+            if (add_prefix)
             {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('l'));  // int32
-                }
-                write_number(static_cast<int32_t>(n));
-            }
-            else if (n <= (std::numeric_limits<int64_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('L'));  // int64
-                }
-                write_number(static_cast<int64_t>(n));
-            }
-            else
-            {
-                JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
+                oa->write_character(static_cast<CharType>('L'));  // int64
             }
+            write_number(static_cast<int64_t>(n));
         }
         else
         {
-            if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('i'));  // int8
-                }
-                write_number(static_cast<int8_t>(n));
-            }
-            else if ((std::numeric_limits<uint8_t>::min)() <= n and n <= (std::numeric_limits<uint8_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('U'));  // uint8
-                }
-                write_number(static_cast<uint8_t>(n));
-            }
-            else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('I'));  // int16
-                }
-                write_number(static_cast<int16_t>(n));
-            }
-            else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('l'));  // int32
-                }
-                write_number(static_cast<int32_t>(n));
-            }
-            else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
-            {
-                if (add_prefix)
-                {
-                    oa->write_character(static_cast<CharType>('L'));  // int64
-                }
-                write_number(static_cast<int64_t>(n));
-            }
-            // LCOV_EXCL_START
-            else
-            {
-                JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
-            }
-            // LCOV_EXCL_STOP
+            JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
         }
     }
 
+    // UBJSON: write number (signed integer)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_signed<NumberType>::value and
+                 not std::is_floating_point<NumberType>::value, int>::type = 0>
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix)
+    {
+        if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('i'));  // int8
+            }
+            write_number(static_cast<int8_t>(n));
+        }
+        else if (static_cast<int64_t>((std::numeric_limits<uint8_t>::min)()) <= n and n <= static_cast<int64_t>((std::numeric_limits<uint8_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('U'));  // uint8
+            }
+            write_number(static_cast<uint8_t>(n));
+        }
+        else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('I'));  // int16
+            }
+            write_number(static_cast<int16_t>(n));
+        }
+        else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('l'));  // int32
+            }
+            write_number(static_cast<int32_t>(n));
+        }
+        else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(static_cast<CharType>('L'));  // int64
+            }
+            write_number(static_cast<int64_t>(n));
+        }
+        // LCOV_EXCL_START
+        else
+        {
+            JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n)));
+        }
+        // LCOV_EXCL_STOP
+    }
+
     /*!
     @brief determine the type prefix of container values
 
@@ -11300,6 +11310,7 @@ class basic_json
     - @a CompatibleType is not derived from `std::istream`,
     - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
          constructors),
+    - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)
     - @a CompatibleType is not a @ref basic_json nested type (e.g.,
          @ref json_pointer, @ref iterator, etc ...)
     - @ref @ref json_serializer<U> has a
@@ -11335,6 +11346,78 @@ class basic_json
         assert_invariant();
     }
 
+    /*!
+    @brief create a JSON value from an existing one
+
+    This is a constructor for existing @ref basic_json types.
+    It does not hijack copy/move constructors, since the parameter has different
+    template arguments than the current ones.
+
+    The constructor tries to convert the internal @ref m_value of the parameter.
+
+    @tparam BasicJsonType a type such that:
+    - @a BasicJsonType is a @ref basic_json type.
+    - @a BasicJsonType has different template arguments than @ref basic_json_t.
+
+    @param[in] val the @ref basic_json value to be converted.
+
+    @complexity Usually linear in the size of the passed @a val, also
+                depending on the implementation of the called `to_json()`
+                method.
+
+    @exceptionsafety Depends on the called constructor. For types directly
+    supported by the library (i.e., all types for which no `to_json()` function
+    was provided), strong guarantee holds: if an exception is thrown, there are
+    no changes to any JSON value.
+
+    @since version 3.1.2
+    */
+    template <typename BasicJsonType,
+              detail::enable_if_t<
+                  detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0>
+    basic_json(const BasicJsonType& val)
+    {
+        using other_boolean_t = typename BasicJsonType::boolean_t;
+        using other_number_float_t = typename BasicJsonType::number_float_t;
+        using other_number_integer_t = typename BasicJsonType::number_integer_t;
+        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+        using other_string_t = typename BasicJsonType::string_t;
+        using other_object_t = typename BasicJsonType::object_t;
+        using other_array_t = typename BasicJsonType::array_t;
+
+        switch (val.type())
+        {
+            case value_t::boolean:
+                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
+                break;
+            case value_t::number_float:
+                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
+                break;
+            case value_t::number_integer:
+                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
+                break;
+            case value_t::number_unsigned:
+                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
+                break;
+            case value_t::string:
+                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
+                break;
+            case value_t::object:
+                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
+                break;
+            case value_t::array:
+                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
+                break;
+            case value_t::null:
+                *this = nullptr;
+                break;
+            case value_t::discarded:
+                m_type = value_t::discarded;
+                break;
+        }
+        assert_invariant();
+    }
+
     /*!
     @brief create a container (array or object) from an initializer list
 
@@ -12507,6 +12590,29 @@ class basic_json
         return *this;
     }
 
+    /*!
+    @brief get special-case overload
+
+    This overloads converts the current @ref basic_json in a different
+    @ref basic_json type
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this, converted into @tparam BasicJsonType
+
+    @complexity Depending on the implementation of the called `from_json()`
+                method.
+
+    @since version 3.1.2
+    */
+    template<typename BasicJsonType, detail::enable_if_t<
+                 not std::is_same<BasicJsonType, basic_json>::value and
+                 detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    BasicJsonType get() const
+    {
+        return *this;
+    }
+
     /*!
     @brief get a value (explicit)
 
@@ -12548,7 +12654,7 @@ class basic_json
     */
     template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
              detail::enable_if_t <
-                 not std::is_same<basic_json_t, ValueType>::value and
+                 not detail::is_basic_json<ValueType>::value and
                  detail::has_from_json<basic_json_t, ValueType>::value and
                  not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
                  int> = 0>
@@ -12814,7 +12920,8 @@ class basic_json
     template < typename ValueType, typename std::enable_if <
                    not std::is_pointer<ValueType>::value and
                    not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
-                   not std::is_same<ValueType, typename string_t::value_type>::value
+                   not std::is_same<ValueType, typename string_t::value_type>::value and
+                   not detail::is_basic_json<ValueType>::value
 #ifndef _MSC_VER  // fix for issue #167 operator<< ambiguity under VS2015
                    and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
 #endif
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index d53d5c4b..e5f6dc55 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -104,6 +104,7 @@ foreach(file ${files})
 
     target_compile_definitions(${testcase} PRIVATE CATCH_CONFIG_FAST_COMPILE)
     target_include_directories(${testcase} PRIVATE "thirdparty/catch")
+    target_include_directories(${testcase} PRIVATE "thirdparty/fifo_map")
     target_include_directories(${testcase} PRIVATE ${NLOHMANN_JSON_INCLUDE_BUILD_DIR})
     target_link_libraries(${testcase} ${NLOHMANN_JSON_TARGET_NAME})
 
diff --git a/test/Makefile b/test/Makefile
index 4dc39916..51edae66 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -4,7 +4,7 @@
 
 # additional flags
 CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Wcast-align -Wcast-qual -Wno-ctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-float-equal
-CPPFLAGS += -I ../single_include -I . -I thirdparty/catch -DCATCH_CONFIG_FAST_COMPILE
+CPPFLAGS += -I ../single_include -I . -I thirdparty/catch -I thirdparty/fifo_map -DCATCH_CONFIG_FAST_COMPILE
 
 SOURCES = src/unit.cpp \
           src/unit-algorithms.cpp \
diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp
index 39753d68..f9dad0d5 100644
--- a/test/src/unit-class_parser.cpp
+++ b/test/src/unit-class_parser.cpp
@@ -1527,6 +1527,89 @@ TEST_CASE("parser class")
             });
             CHECK(j_empty_array == json());
         }
+
+        SECTION("skip in GeoJSON")
+        {
+            auto geojsonExample = R"(
+              { "type": "FeatureCollection",
+                "features": [
+                  { "type": "Feature",
+                    "geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
+                    "properties": {"prop0": "value0"}
+                    },
+                  { "type": "Feature",
+                    "geometry": {
+                      "type": "LineString",
+                      "coordinates": [
+                        [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
+                        ]
+                      },
+                    "properties": {
+                      "prop0": "value0",
+                      "prop1": 0.0
+                      }
+                    },
+                  { "type": "Feature",
+                     "geometry": {
+                       "type": "Polygon",
+                       "coordinates": [
+                         [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
+                           [100.0, 1.0], [100.0, 0.0] ]
+                         ]
+                     },
+                     "properties": {
+                       "prop0": "value0",
+                       "prop1": {"this": "that"}
+                       }
+                     }
+                   ]
+                 })";
+
+            json::parser_callback_t cb = [&](int depth, json::parse_event_t event, json & parsed)
+            {
+                // skip uninteresting events
+                if (event == json::parse_event_t::value and !parsed.is_primitive())
+                {
+                    return false;
+                }
+
+                switch (event)
+                {
+                    case json::parse_event_t::key:
+                    {
+                        return true;
+                    }
+                    case json::parse_event_t::value:
+                    {
+                        return false;
+                    }
+                    case json::parse_event_t::object_start:
+                    {
+                        return true;
+                    }
+                    case json::parse_event_t::object_end:
+                    {
+                        return false;
+                    }
+                    case json::parse_event_t::array_start:
+                    {
+                        return true;
+                    }
+                    case json::parse_event_t::array_end:
+                    {
+                        return false;
+                    }
+
+                    default:
+                    {
+                        return true;
+                    }
+                }
+            };
+
+            auto j = json::parse(geojsonExample, cb, true);
+            CHECK(j == json());
+        }
     }
 
     SECTION("constructing from contiguous containers")
diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp
index 97bdf0c1..c84c2750 100644
--- a/test/src/unit-regression.cpp
+++ b/test/src/unit-regression.cpp
@@ -32,10 +32,72 @@ SOFTWARE.
 #include <nlohmann/json.hpp>
 using nlohmann::json;
 
+#include "fifo_map.hpp"
+
 #include <fstream>
 #include <list>
 #include <cstdio>
 
+/////////////////////////////////////////////////////////////////////
+// for #972
+/////////////////////////////////////////////////////////////////////
+
+template<class K, class V, class dummy_compare, class A>
+using my_workaround_fifo_map = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<K>, A>;
+using my_json = nlohmann::basic_json<my_workaround_fifo_map>;
+
+/////////////////////////////////////////////////////////////////////
+// for #977
+/////////////////////////////////////////////////////////////////////
+
+namespace ns
+{
+struct foo
+{
+    int x;
+};
+
+template <typename, typename SFINAE = void>
+struct foo_serializer;
+
+template<typename T>
+struct foo_serializer<T, typename std::enable_if<std::is_same<foo, T>::value>::type>
+{
+    template <typename BasicJsonType>
+    static void to_json(BasicJsonType& j, const T& value)
+    {
+        j = BasicJsonType{{"x", value.x}};
+    }
+    template <typename BasicJsonType>
+    static void from_json(const BasicJsonType& j, T& value)     // !!!
+    {
+        nlohmann::from_json(j.at("x"), value.x);
+    }
+};
+
+template<typename T>
+struct foo_serializer < T, typename std::enable_if < !std::is_same<foo, T>::value >::type >
+{
+    template <typename BasicJsonType>
+    static void to_json(BasicJsonType& j, const T& value) noexcept
+    {
+        ::nlohmann::to_json(j, value);
+    }
+    template <typename BasicJsonType>
+    static void from_json(const BasicJsonType& j, T& value)   //!!!
+    {
+        ::nlohmann::from_json(j, value);
+    }
+};
+}
+
+using foo_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t,
+      std::uint64_t, double, std::allocator, ns::foo_serializer>;
+
+/////////////////////////////////////////////////////////////////////
+// for #805
+/////////////////////////////////////////////////////////////////////
+
 namespace
 {
 struct nocopy
@@ -1436,4 +1498,20 @@ TEST_CASE("regression tests")
         //CHECK_THROWS_WITH(json::from_ubjson(v_ubjson),
         //                  "[json.exception.out_of_range.408] excessive object size: 8658170730974374167");
     }
+
+    SECTION("issue #972 - Segmentation fault on G++ when trying to assign json string literal to custom json type")
+    {
+        my_json foo = R"([1, 2, 3])"_json;
+    }
+
+    SECTION("issue #977 - Assigning between different json types")
+    {
+        foo_json lj = ns::foo{3};
+        ns::foo ff = lj;
+        CHECK(lj.is_object());
+        CHECK(lj.size() == 1);
+        CHECK(lj["x"] == 3);
+        CHECK(ff.x == 3);
+        nlohmann::json nj = lj;                // This line works as expected
+    }
 }
diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp
index f270fdc4..8a5ac73f 100644
--- a/test/src/unit-udt.cpp
+++ b/test/src/unit-udt.cpp
@@ -693,6 +693,83 @@ TEST_CASE("custom serializer that does adl by default", "[udt]")
     CHECK(me == cj.get<udt::person>());
 }
 
+TEST_CASE("different basic_json types conversions")
+{
+    using json = nlohmann::json;
+
+    SECTION("null")
+    {
+        json j;
+        custom_json cj = j;
+        CHECK(cj == nullptr);
+    }
+
+    SECTION("boolean")
+    {
+        json j = true;
+        custom_json cj = j;
+        CHECK(cj == true);
+    }
+
+    SECTION("discarded")
+    {
+        json j(json::value_t::discarded);
+        custom_json cj;
+        CHECK_NOTHROW(cj = j);
+        CHECK(cj.type() == custom_json::value_t::discarded);
+    }
+
+    SECTION("array")
+    {
+        json j = {1, 2, 3};
+        custom_json cj = j;
+        CHECK((cj == std::vector<int> {1, 2, 3}));
+    }
+
+    SECTION("integer")
+    {
+        json j = 42;
+        custom_json cj = j;
+        CHECK(cj == 42);
+    }
+
+    SECTION("float")
+    {
+        json j = 42.0;
+        custom_json cj = j;
+        CHECK(cj == 42.0);
+    }
+
+    SECTION("unsigned")
+    {
+        json j = 42u;
+        custom_json cj = j;
+        CHECK(cj == 42u);
+    }
+
+    SECTION("string")
+    {
+        json j = "forty-two";
+        custom_json cj = j;
+        CHECK(cj == "forty-two");
+    }
+
+    SECTION("object")
+    {
+        json j = {{"forty", "two"}};
+        custom_json cj = j;
+        auto m = j.get<std::map<std::string, std::string>>();
+        CHECK(cj == m);
+    }
+
+    SECTION("get<custom_json>")
+    {
+        json j = 42;
+        custom_json cj = j.get<custom_json>();
+        CHECK(cj == 42);
+    }
+}
+
 namespace
 {
 struct incomplete;
@@ -730,6 +807,6 @@ TEST_CASE("Issue #924")
     // Prevent get<std::vector<Evil>>() to throw
     auto j = json::array();
 
-    (void) j.get<Evil>();
-    (void) j.get<std::vector<Evil>>();
+    CHECK_NOTHROW(j.get<Evil>());
+    CHECK_NOTHROW(j.get<std::vector<Evil>>());
 }
diff --git a/test/thirdparty/fifo_map/LICENSE.MIT b/test/thirdparty/fifo_map/LICENSE.MIT
new file mode 100644
index 00000000..8c59cdf2
--- /dev/null
+++ b/test/thirdparty/fifo_map/LICENSE.MIT
@@ -0,0 +1,21 @@
+MIT License 
+
+Copyright (c) 2015-2017 Niels Lohmann
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/test/thirdparty/fifo_map/fifo_map.hpp b/test/thirdparty/fifo_map/fifo_map.hpp
new file mode 100644
index 00000000..c281e3be
--- /dev/null
+++ b/test/thirdparty/fifo_map/fifo_map.hpp
@@ -0,0 +1,530 @@
+/*
+The code is licensed under the MIT License <http://opensource.org/licenses/MIT>:
+
+Copyright (c) 2015-2017 Niels Lohmann.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef NLOHMANN_FIFO_MAP_HPP
+#define NLOHMANN_FIFO_MAP_HPP
+
+#include <algorithm>
+#include <cstdlib>
+#include <functional>
+#include <iostream>
+#include <limits>
+#include <map>
+#include <memory>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+*/
+namespace nlohmann
+{
+
+template<class Key>
+class fifo_map_compare
+{
+  public:
+    /// constructor given a pointer to a key storage
+    fifo_map_compare(std::unordered_map<Key, std::size_t>* k) : keys(k) {}
+
+    /*!
+    This function compares two keys with respect to the order in which they
+    were added to the container. For this, the mapping keys is used.
+    */
+    bool operator()(const Key& lhs, const Key& rhs) const
+    {
+        // look up timestamps for both keys
+        const auto timestamp_lhs = keys->find(lhs);
+        const auto timestamp_rhs = keys->find(rhs);
+
+        if (timestamp_lhs == keys->end())
+        {
+            // timestamp for lhs not found - cannot be smaller than for rhs
+            return false;
+        }
+
+        if (timestamp_rhs == keys->end())
+        {
+            // timestamp for rhs not found - timestamp for lhs is smaller
+            return true;
+        }
+
+        // compare timestamps
+        return timestamp_lhs->second < timestamp_rhs->second;
+    }
+
+    void add_key(const Key& key)
+    {
+        keys->insert({key, timestamp++});
+    }
+
+    void remove_key(const Key& key)
+    {
+        keys->erase(key);
+    }
+
+  private:
+    /// pointer to a mapping from keys to insertion timestamps
+    std::unordered_map<Key, std::size_t>* keys = nullptr;
+    /// the next valid insertion timestamp
+    size_t timestamp = 1;
+};
+
+
+template <
+    class Key,
+    class T,
+    class Compare = fifo_map_compare<Key>,
+    class Allocator = std::allocator<std::pair<const Key, T>>
+    > class fifo_map
+{
+  public:
+    using key_type = Key;
+    using mapped_type = T;
+    using value_type = std::pair<const Key, T>;
+    using size_type = std::size_t;
+    using difference_type = std::ptrdiff_t;
+    using key_compare = Compare;
+    using allocator_type = Allocator;
+    using reference = value_type&;
+    using const_reference = const value_type&;
+    using pointer = typename std::allocator_traits<Allocator>::pointer;
+    using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
+
+    using internal_map_type = std::map<Key, T, Compare, Allocator>;
+
+    using iterator = typename internal_map_type::iterator;
+    using const_iterator = typename internal_map_type::const_iterator;
+    using reverse_iterator = typename internal_map_type::reverse_iterator;
+    using const_reverse_iterator = typename internal_map_type::const_reverse_iterator;
+
+  public:
+    /// default constructor
+    fifo_map() : m_keys(), m_compare(&m_keys), m_map(m_compare) {}
+
+    /// copy constructor
+    fifo_map(const fifo_map &f) : m_keys(f.m_keys), m_compare(&m_keys), m_map(f.m_map.begin(), f.m_map.end(), m_compare) {}
+
+    /// constructor for a range of elements
+    template<class InputIterator>
+    fifo_map(InputIterator first, InputIterator last)
+        : m_keys(), m_compare(&m_keys), m_map(m_compare)
+    {
+        for (auto it = first; it != last; ++it)
+        {
+            insert(*it);
+        }
+    }
+
+    /// constructor for a list of elements
+    fifo_map(std::initializer_list<value_type> init) : fifo_map()
+    {
+        for (auto x : init)
+        {
+            insert(x);
+        }
+    }
+
+
+    /*
+     * Element access
+     */
+
+    /// access specified element with bounds checking
+    T& at(const Key& key)
+    {
+        return m_map.at(key);
+    }
+
+    /// access specified element with bounds checking
+    const T& at(const Key& key) const
+    {
+        return m_map.at(key);
+    }
+
+    /// access specified element
+    T& operator[](const Key& key)
+    {
+        m_compare.add_key(key);
+        return m_map[key];
+    }
+
+    /// access specified element
+    T& operator[](Key&& key)
+    {
+        m_compare.add_key(key);
+        return m_map[key];
+    }
+
+
+    /*
+     * Iterators
+     */
+
+    /// returns an iterator to the beginning
+    iterator begin() noexcept
+    {
+        return m_map.begin();
+    }
+
+    /// returns an iterator to the end
+    iterator end() noexcept
+    {
+        return m_map.end();
+    }
+
+    /// returns an iterator to the beginning
+    const_iterator begin() const noexcept
+    {
+        return m_map.begin();
+    }
+
+    /// returns an iterator to the end
+    const_iterator end() const noexcept
+    {
+        return m_map.end();
+    }
+
+    /// returns an iterator to the beginning
+    const_iterator cbegin() const noexcept
+    {
+        return m_map.cbegin();
+    }
+
+    /// returns an iterator to the end
+    const_iterator cend() const noexcept
+    {
+        return m_map.cend();
+    }
+
+    /// returns a reverse iterator to the beginning
+    reverse_iterator rbegin() noexcept
+    {
+        return m_map.rbegin();
+    }
+
+    /// returns a reverse iterator to the end
+    reverse_iterator rend() noexcept
+    {
+        return m_map.rend();
+    }
+
+    /// returns a reverse iterator to the beginning
+    const_reverse_iterator rbegin() const noexcept
+    {
+        return m_map.rbegin();
+    }
+
+    /// returns a reverse iterator to the end
+    const_reverse_iterator rend() const noexcept
+    {
+        return m_map.rend();
+    }
+
+    /// returns a reverse iterator to the beginning
+    const_reverse_iterator crbegin() const noexcept
+    {
+        return m_map.crbegin();
+    }
+
+    /// returns a reverse iterator to the end
+    const_reverse_iterator crend() const noexcept
+    {
+        return m_map.crend();
+    }
+
+
+    /*
+     * Capacity
+     */
+
+    /// checks whether the container is empty
+    bool empty() const noexcept
+    {
+        return m_map.empty();
+    }
+
+    /// returns the number of elements
+    size_type size() const noexcept
+    {
+        return m_map.size();
+    }
+
+    /// returns the maximum possible number of elements
+    size_type max_size() const noexcept
+    {
+        return m_map.max_size();
+    }
+
+
+    /*
+     * Modifiers
+     */
+
+    /// clears the contents
+    void clear() noexcept
+    {
+        m_map.clear();
+        m_keys.clear();
+    }
+
+    /// insert value
+    std::pair<iterator, bool> insert(const value_type& value)
+    {
+        m_compare.add_key(value.first);
+        return m_map.insert(value);
+    }
+
+    /// insert value
+    template<class P>
+    std::pair<iterator, bool> insert( P&& value )
+    {
+        m_compare.add_key(value.first);
+        return m_map.insert(value);
+    }
+
+    /// insert value with hint
+    iterator insert(const_iterator hint, const value_type& value)
+    {
+        m_compare.add_key(value.first);
+        return m_map.insert(hint, value);
+    }
+
+    /// insert value with hint
+    iterator insert(const_iterator hint, value_type&& value)
+    {
+        m_compare.add_key(value.first);
+        return m_map.insert(hint, value);
+    }
+
+    /// insert value range
+    template<class InputIt>
+    void insert(InputIt first, InputIt last)
+    {
+        for (const_iterator it = first; it != last; ++it)
+        {
+            m_compare.add_key(it->first);
+        }
+
+        m_map.insert(first, last);
+    }
+
+    /// insert value list
+    void insert(std::initializer_list<value_type> ilist)
+    {
+        for (auto value : ilist)
+        {
+            m_compare.add_key(value.first);
+        }
+
+        m_map.insert(ilist);
+    }
+
+    /// constructs element in-place
+    template<class... Args>
+    std::pair<iterator, bool> emplace(Args&& ... args)
+    {
+        typename fifo_map::value_type value(std::forward<Args>(args)...);
+        m_compare.add_key(value.first);
+        return m_map.emplace(std::move(value));
+    }
+
+    /// constructs element in-place with hint
+    template<class... Args>
+    iterator emplace_hint(const_iterator hint, Args&& ... args)
+    {
+        typename fifo_map::value_type value(std::forward<Args>(args)...);
+        m_compare.add_key(value.first);
+        return m_map.emplace_hint(hint, std::move(value));
+    }
+
+    /// remove element at position
+    iterator erase(const_iterator pos)
+    {
+        m_compare.remove_key(pos->first);
+        return m_map.erase(pos);
+    }
+
+    /// remove elements in range
+    iterator erase(const_iterator first, const_iterator last)
+    {
+        for (const_iterator it = first; it != last; ++it)
+        {
+            m_compare.remove_key(it->first);
+        }
+
+        return m_map.erase(first, last);
+    }
+
+    /// remove elements with key
+    size_type erase(const key_type& key)
+    {
+        size_type res = m_map.erase(key);
+
+        if (res > 0)
+        {
+            m_compare.remove_key(key);
+        }
+
+        return res;
+    }
+
+    /// swaps the contents
+    void swap(fifo_map& other)
+    {
+        std::swap(m_map, other.m_map);
+        std::swap(m_compare, other.m_compare);
+        std::swap(m_keys, other.m_keys);
+    }
+
+
+    /*
+     * Lookup
+     */
+
+    /// returns the number of elements matching specific key
+    size_type count(const Key& key) const
+    {
+        return m_map.count(key);
+    }
+
+    /// finds element with specific key
+    iterator find(const Key& key)
+    {
+        return m_map.find(key);
+    }
+
+    /// finds element with specific key
+    const_iterator find(const Key& key) const
+    {
+        return m_map.find(key);
+    }
+
+    /// returns range of elements matching a specific key
+    std::pair<iterator, iterator> equal_range(const Key& key)
+    {
+        return m_map.equal_range(key);
+    }
+
+    /// returns range of elements matching a specific key
+    std::pair<const_iterator, const_iterator> equal_range(const Key& key) const
+    {
+        return m_map.equal_range(key);
+    }
+
+    /// returns an iterator to the first element not less than the given key
+    iterator lower_bound(const Key& key)
+    {
+        return m_map.lower_bound(key);
+    }
+
+    /// returns an iterator to the first element not less than the given key
+    const_iterator lower_bound(const Key& key) const
+    {
+        return m_map.lower_bound(key);
+    }
+
+    /// returns an iterator to the first element greater than the given key
+    iterator upper_bound(const Key& key)
+    {
+        return m_map.upper_bound(key);
+    }
+
+    /// returns an iterator to the first element greater than the given key
+    const_iterator upper_bound(const Key& key) const
+    {
+        return m_map.upper_bound(key);
+    }
+
+
+    /*
+     * Observers
+     */
+
+    /// returns the function that compares keys
+    key_compare key_comp() const
+    {
+        return m_compare;
+    }
+
+
+    /*
+     * Non-member functions
+     */
+
+    friend bool operator==(const fifo_map& lhs, const fifo_map& rhs)
+    {
+        return lhs.m_map == rhs.m_map;
+    }
+
+    friend bool operator!=(const fifo_map& lhs, const fifo_map& rhs)
+    {
+        return lhs.m_map != rhs.m_map;
+    }
+
+    friend bool operator<(const fifo_map& lhs, const fifo_map& rhs)
+    {
+        return lhs.m_map < rhs.m_map;
+    }
+
+    friend bool operator<=(const fifo_map& lhs, const fifo_map& rhs)
+    {
+        return lhs.m_map <= rhs.m_map;
+    }
+
+    friend bool operator>(const fifo_map& lhs, const fifo_map& rhs)
+    {
+        return lhs.m_map > rhs.m_map;
+    }
+
+    friend bool operator>=(const fifo_map& lhs, const fifo_map& rhs)
+    {
+        return lhs.m_map >= rhs.m_map;
+    }
+
+  private:
+    /// the keys
+    std::unordered_map<Key, std::size_t> m_keys;
+    /// the comparison object
+    Compare m_compare;
+    /// the internal data structure
+    internal_map_type m_map;
+};
+
+}
+
+// specialization of std::swap
+namespace std
+{
+template <class Key, class T, class Compare, class Allocator>
+inline void swap(nlohmann::fifo_map<Key, T, Compare, Allocator>& m1,
+                 nlohmann::fifo_map<Key, T, Compare, Allocator>& m2)
+{
+    m1.swap(m2);
+}
+}
+
+#endif