Merge branch 'develop' into feature/userdefined_exceptions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c0488b3..72802b2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 3.0)
 
 # define the project
-project(nlohmann_json VERSION 2.0.0 LANGUAGES CXX)
+project(nlohmann_json VERSION 2.0.2 LANGUAGES CXX)
 
 enable_testing()
 
diff --git a/ChangeLog.md b/ChangeLog.md
index 901e37c..8062f3b 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,9 +1,8 @@
 # Change Log
 All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
 
-## [Unreleased](https://github.com/nlohmann/json/tree/HEAD)
-
-[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...HEAD)
+## [v2.0.2](https://github.com/nlohmann/json/releases/tag/v2.0.2) (2016-07-30)
+[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...v2.0.2)
 
 - value\(\) does not work with \_json\_pointer types [\#283](https://github.com/nlohmann/json/issues/283)
 - Easy serialization of classes [\#280](https://github.com/nlohmann/json/issues/280)
diff --git a/Makefile b/Makefile
index 98f0768..15d00f6 100644
--- a/Makefile
+++ b/Makefile
@@ -95,7 +95,9 @@
 # changelog
 ##########################################################################
 
+NEXT_VERSION ?= "unreleased"
+
 ChangeLog.md:
-	github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s
+	github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s --future-release $(NEXT_VERSION)
 	gsed -i 's|https://github.com/nlohmann/json/releases/tag/HEAD|https://github.com/nlohmann/json/tree/HEAD|' ChangeLog.md
 	gsed -i '2i All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).' ChangeLog.md
diff --git a/README.md b/README.md
index e16d881..6b6b6f9 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
 [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
 [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
-[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/wuiuqYiYqRTdI3rG)
+[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN)
 [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
 [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
 [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
diff --git a/doc/Doxyfile b/doc/Doxyfile
index e74a8a8..e0055d6 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -5,7 +5,7 @@
 #---------------------------------------------------------------------------
 DOXYFILE_ENCODING      = UTF-8
 PROJECT_NAME           = "JSON for Modern C++"
-PROJECT_NUMBER         = 2.0.0
+PROJECT_NUMBER         = 2.0.2
 PROJECT_BRIEF          = 
 PROJECT_LOGO           =
 OUTPUT_DIRECTORY       = .
diff --git a/doc/examples/README.link b/doc/examples/README.link
index ed559e1..0e87a79 100644
--- a/doc/examples/README.link
+++ b/doc/examples/README.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/k5KRwVJ42VggxxzX"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/index.md b/doc/index.md
index 5373d4f..cf32a7a 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -268,4 +268,4 @@
 @author [Niels Lohmann](http://nlohmann.me)
 @see https://github.com/nlohmann/json to download the source code
 
-@version 2.0.0
+@version 2.0.2
diff --git a/doc/json.gif b/doc/json.gif
index 46f005d..62a1a2e 100644
--- a/doc/json.gif
+++ b/doc/json.gif
Binary files differ
diff --git a/src/json.hpp b/src/json.hpp
index 4de4ff4..a95717c 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++
-|  |  |__   |  |  | | | |  version 3.0.0
+|  |  |__   |  |  | | | |  version 2.0.2
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -190,6 +190,13 @@
    JSON values can be used like STL containers and provide reverse iterator
    access.
 
+@invariant The member variables @a m_value and @a m_type have the following
+relationship:
+- If `m_type == value_t::object`, then `m_value.object != nullptr`.
+- If `m_type == value_t::array`, then `m_value.array != nullptr`.
+- If `m_type == value_t::string`, then `m_value.string != nullptr`.
+The invariants are checked by member function assert_invariant().
+
 @internal
 @note ObjectType trick from http://stackoverflow.com/a/9860911
 @endinternal
@@ -985,6 +992,21 @@
         }
     };
 
+    /*!
+    @brief checks the class invariants
+
+    This function asserts the class invariants. It needs to be called at the
+    end of every constructor to make sure that created objects respect the
+    invariant. Furthermore, it has to be called each time the type of a JSON
+    value is changed, because the invariant expresses a relationship between
+    @a m_type and @a m_value.
+    */
+    void assert_invariant() const
+    {
+        assert(m_type != value_t::object or m_value.object != nullptr);
+        assert(m_type != value_t::array or m_value.array != nullptr);
+        assert(m_type != value_t::string or m_value.string != nullptr);
+    }
 
   public:
     //////////////////////////
@@ -1124,7 +1146,9 @@
     */
     basic_json(const value_t value_type)
         : m_type(value_type), m_value(value_type)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a null object (implicitly)
@@ -1132,6 +1156,9 @@
     Create a `null` JSON value. This is the implicit version of the `null`
     value constructor as it takes no parameters.
 
+    @note The class invariant is satisfied, because it poses no requirements
+    for null values.
+
     @complexity Constant.
 
     @exceptionsafety No-throw guarantee: this constructor never throws
@@ -1176,7 +1203,9 @@
     */
     basic_json(std::nullptr_t) noexcept
         : basic_json(value_t::null)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an object (explicit)
@@ -1199,7 +1228,9 @@
     */
     basic_json(const object_t& val)
         : m_type(value_t::object), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an object (implicit)
@@ -1238,6 +1269,7 @@
         using std::begin;
         using std::end;
         m_value.object = create<object_t>(begin(val), end(val));
+        assert_invariant();
     }
 
     /*!
@@ -1261,7 +1293,9 @@
     */
     basic_json(const array_t& val)
         : m_type(value_t::array), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an array (implicit)
@@ -1305,6 +1339,7 @@
         using std::begin;
         using std::end;
         m_value.array = create<array_t>(begin(val), end(val));
+        assert_invariant();
     }
 
     /*!
@@ -1330,7 +1365,9 @@
     */
     basic_json(const string_t& val)
         : m_type(value_t::string), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a string (explicit)
@@ -1354,7 +1391,9 @@
     */
     basic_json(const typename string_t::value_type* val)
         : basic_json(string_t(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a string (implicit)
@@ -1385,7 +1424,9 @@
               = 0>
     basic_json(const CompatibleStringType& val)
         : basic_json(string_t(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a boolean (explicit)
@@ -1403,7 +1444,9 @@
     */
     basic_json(boolean_t val) noexcept
         : m_type(value_t::boolean), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an integer number (explicit)
@@ -1436,7 +1479,9 @@
              = 0>
     basic_json(const number_integer_t val) noexcept
         : m_type(value_t::number_integer), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an integer number from an enum type (explicit)
@@ -1466,7 +1511,9 @@
     basic_json(const int val) noexcept
         : m_type(value_t::number_integer),
           m_value(static_cast<number_integer_t>(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an integer number (implicit)
@@ -1503,7 +1550,9 @@
     basic_json(const CompatibleNumberIntegerType val) noexcept
         : m_type(value_t::number_integer),
           m_value(static_cast<number_integer_t>(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an unsigned integer number (explicit)
@@ -1530,7 +1579,9 @@
              = 0>
     basic_json(const number_unsigned_t val) noexcept
         : m_type(value_t::number_unsigned), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an unsigned number (implicit)
@@ -1562,7 +1613,9 @@
     basic_json(const CompatibleNumberUnsignedType val) noexcept
         : m_type(value_t::number_unsigned),
           m_value(static_cast<number_unsigned_t>(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a floating-point number (explicit)
@@ -1597,6 +1650,8 @@
             m_type = value_t::null;
             m_value = json_value();
         }
+
+        assert_invariant();
     }
 
     /*!
@@ -1637,7 +1692,9 @@
              >
     basic_json(const CompatibleNumberFloatType val) noexcept
         : basic_json(number_float_t(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a container (array or object) from an initializer list
@@ -1742,8 +1799,6 @@
             m_type = value_t::object;
             m_value = value_t::object;
 
-            assert(m_value.object != nullptr);
-
             std::for_each(init.begin(), init.end(), [this](const basic_json & element)
             {
                 m_value.object->emplace(*(element[0].m_value.string), element[1]);
@@ -1755,6 +1810,8 @@
             m_type = value_t::array;
             m_value.array = create<array_t>(init);
         }
+
+        assert_invariant();
     }
 
     /*!
@@ -1859,6 +1916,7 @@
         : m_type(value_t::array)
     {
         m_value.array = create<array_t>(cnt, val);
+        assert_invariant();
     }
 
     /*!
@@ -1879,12 +1937,14 @@
     @param[in] first begin of the range to copy from (included)
     @param[in] last end of the range to copy from (excluded)
 
+    @pre Iterators @a first and @a last must be initialized.
+
     @throw invalid_iterator (101) if iterators are not compatible; that is, do
     not belong to the same JSON value; example: `"iterators are not
     compatible"`
-    @throw invalid_iterator (104) if iterators are for a primitive type (number,
-    boolean, or string) where an out of range error can be detected easily;
-    example: `"iterators out of range"`
+    @throw invalid_iterator (104) if iterators are for a primitive type
+    (number, boolean, or string) where an out of range error can be detected
+    easily; example: `"iterators out of range"`
     @throw std::bad_alloc if allocation for object, array, or string fails
     @throw invalid_iterator (106) if called with a null value; example: `"cannot
     construct with iterators from null"`
@@ -1902,14 +1962,20 @@
                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value
                   , int>::type
               = 0>
-    basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type)
+    basic_json(InputIT first, InputIT last)
     {
+        assert(first.m_object != nullptr);
+        assert(last.m_object != nullptr);
+
         // make sure iterator fits the current value
         if (first.m_object != last.m_object)
         {
             throw invalid_iterator(101, "iterators are not compatible");
         }
 
+        // copy type from first iterator
+        m_type = first.m_object->m_type;
+
         // check if iterator range is complete for primitive values
         switch (m_type)
         {
@@ -1936,35 +2002,30 @@
         {
             case value_t::number_integer:
             {
-                assert(first.m_object != nullptr);
                 m_value.number_integer = first.m_object->m_value.number_integer;
                 break;
             }
 
             case value_t::number_unsigned:
             {
-                assert(first.m_object != nullptr);
                 m_value.number_unsigned = first.m_object->m_value.number_unsigned;
                 break;
             }
 
             case value_t::number_float:
             {
-                assert(first.m_object != nullptr);
                 m_value.number_float = first.m_object->m_value.number_float;
                 break;
             }
 
             case value_t::boolean:
             {
-                assert(first.m_object != nullptr);
                 m_value.boolean = first.m_object->m_value.boolean;
                 break;
             }
 
             case value_t::string:
             {
-                assert(first.m_object != nullptr);
                 m_value = *first.m_object->m_value.string;
                 break;
             }
@@ -1988,6 +2049,8 @@
                                        first.m_object->type_name());
             }
         }
+
+        assert_invariant();
     }
 
     /*!
@@ -2013,6 +2076,7 @@
     explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)
     {
         *this = parser(i, cb).parse();
+        assert_invariant();
     }
 
     ///////////////////////////////////////
@@ -2044,25 +2108,25 @@
     basic_json(const basic_json& other)
         : m_type(other.m_type)
     {
+        // check of passed value is valid
+        other.assert_invariant();
+
         switch (m_type)
         {
             case value_t::object:
             {
-                assert(other.m_value.object != nullptr);
                 m_value = *other.m_value.object;
                 break;
             }
 
             case value_t::array:
             {
-                assert(other.m_value.array != nullptr);
                 m_value = *other.m_value.array;
                 break;
             }
 
             case value_t::string:
             {
-                assert(other.m_value.string != nullptr);
                 m_value = *other.m_value.string;
                 break;
             }
@@ -2096,6 +2160,8 @@
                 break;
             }
         }
+
+        assert_invariant();
     }
 
     /*!
@@ -2120,9 +2186,14 @@
         : m_type(std::move(other.m_type)),
           m_value(std::move(other.m_value))
     {
+        // check that passed value is valid
+        other.assert_invariant();
+
         // invalidate payload
         other.m_type = value_t::null;
         other.m_value = {};
+
+        assert_invariant();
     }
 
     /*!
@@ -2155,9 +2226,14 @@
         std::is_nothrow_move_assignable<json_value>::value
     )
     {
+        // check that passed value is valid
+        other.assert_invariant();
+
         using std::swap;
         swap(m_type, other.m_type);
         swap(m_value, other.m_value);
+
+        assert_invariant();
         return *this;
     }
 
@@ -2178,6 +2254,8 @@
     */
     ~basic_json()
     {
+        assert_invariant();
+
         switch (m_type)
         {
             case value_t::object:
@@ -2670,7 +2748,6 @@
     {
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return T(m_value.object->begin(), m_value.object->end());
         }
         else
@@ -2684,7 +2761,6 @@
     {
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return *(m_value.object);
         }
         else
@@ -2707,7 +2783,6 @@
         if (is_array())
         {
             T to_vector;
-            assert(m_value.array != nullptr);
             std::transform(m_value.array->begin(), m_value.array->end(),
                            std::inserter(to_vector, to_vector.end()), [](basic_json i)
             {
@@ -2732,7 +2807,6 @@
         if (is_array())
         {
             std::vector<T> to_vector;
-            assert(m_value.array != nullptr);
             to_vector.reserve(m_value.array->size());
             std::transform(m_value.array->begin(), m_value.array->end(),
                            std::inserter(to_vector, to_vector.end()), [](basic_json i)
@@ -2757,7 +2831,6 @@
     {
         if (is_array())
         {
-            assert(m_value.array != nullptr);
             return T(m_value.array->begin(), m_value.array->end());
         }
         else
@@ -2771,7 +2844,6 @@
     {
         if (is_array())
         {
-            assert(m_value.array != nullptr);
             return *(m_value.array);
         }
         else
@@ -2789,7 +2861,6 @@
     {
         if (is_string())
         {
-            assert(m_value.string != nullptr);
             return *m_value.string;
         }
         else
@@ -3266,7 +3337,6 @@
         {
             try
             {
-                assert(m_value.array != nullptr);
                 return m_value.array->at(idx);
             }
             catch (std::out_of_range&)
@@ -3310,7 +3380,6 @@
         {
             try
             {
-                assert(m_value.array != nullptr);
                 return m_value.array->at(idx);
             }
             catch (std::out_of_range&)
@@ -3358,7 +3427,6 @@
         {
             try
             {
-                assert(m_value.object != nullptr);
                 return m_value.object->at(key);
             }
             catch (std::out_of_range&)
@@ -3406,7 +3474,6 @@
         {
             try
             {
-                assert(m_value.object != nullptr);
                 return m_value.object->at(key);
             }
             catch (std::out_of_range&)
@@ -3453,13 +3520,13 @@
         {
             m_type = value_t::array;
             m_value.array = create<array_t>();
+            assert_invariant();
         }
 
         // operator[] only works for arrays
         if (is_array())
         {
             // fill up array with null values if given idx is outside range
-            assert(m_value.array != nullptr);
             if (idx >= m_value.array->size())
             {
                 m_value.array->insert(m_value.array->end(),
@@ -3499,7 +3566,6 @@
         // const operator[] only works for arrays
         if (is_array())
         {
-            assert(m_value.array != nullptr);
             return m_value.array->operator[](idx);
         }
         else
@@ -3542,12 +3608,12 @@
         {
             m_type = value_t::object;
             m_value.object = create<object_t>();
+            assert_invariant();
         }
 
         // operator[] only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return m_value.object->operator[](key);
         }
         else
@@ -3588,7 +3654,6 @@
         // const operator[] only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             assert(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
@@ -3701,12 +3766,12 @@
         {
             m_type = value_t::object;
             m_value = value_t::object;
+            assert_invariant();
         }
 
         // at only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return m_value.object->operator[](key);
         }
         else
@@ -3748,7 +3813,6 @@
         // at only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             assert(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
@@ -4076,24 +4140,25 @@
 
                 if (is_string())
                 {
-                    delete m_value.string;
+                    AllocatorType<string_t> alloc;
+                    alloc.destroy(m_value.string);
+                    alloc.deallocate(m_value.string, 1);
                     m_value.string = nullptr;
                 }
 
                 m_type = value_t::null;
+                assert_invariant();
                 break;
             }
 
             case value_t::object:
             {
-                assert(m_value.object != nullptr);
                 result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
                 break;
             }
 
             case value_t::array:
             {
-                assert(m_value.array != nullptr);
                 result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
                 break;
             }
@@ -4184,17 +4249,19 @@
 
                 if (is_string())
                 {
-                    delete m_value.string;
+                    AllocatorType<string_t> alloc;
+                    alloc.destroy(m_value.string);
+                    alloc.deallocate(m_value.string, 1);
                     m_value.string = nullptr;
                 }
 
                 m_type = value_t::null;
+                assert_invariant();
                 break;
             }
 
             case value_t::object:
             {
-                assert(m_value.object != nullptr);
                 result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
                                               last.m_it.object_iterator);
                 break;
@@ -4202,7 +4269,6 @@
 
             case value_t::array:
             {
-                assert(m_value.array != nullptr);
                 result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
                                              last.m_it.array_iterator);
                 break;
@@ -4251,7 +4317,6 @@
         // this erase only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return m_value.object->erase(key);
         }
         else
@@ -4294,7 +4359,6 @@
                 throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
             }
 
-            assert(m_value.array != nullptr);
             m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
         }
         else
@@ -4337,7 +4401,6 @@
 
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             result.m_it.object_iterator = m_value.object->find(key);
         }
 
@@ -4354,7 +4417,6 @@
 
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             result.m_it.object_iterator = m_value.object->find(key);
         }
 
@@ -4382,7 +4444,6 @@
     size_type count(typename object_t::key_type key) const
     {
         // return 0 for all nonobject types
-        assert(not is_object() or m_value.object != nullptr);
         return is_object() ? m_value.object->count(key) : 0;
     }
 
@@ -4758,14 +4819,12 @@
             case value_t::array:
             {
                 // delegate call to array_t::empty()
-                assert(m_value.array != nullptr);
                 return m_value.array->empty();
             }
 
             case value_t::object:
             {
                 // delegate call to object_t::empty()
-                assert(m_value.object != nullptr);
                 return m_value.object->empty();
             }
 
@@ -4828,14 +4887,12 @@
             case value_t::array:
             {
                 // delegate call to array_t::size()
-                assert(m_value.array != nullptr);
                 return m_value.array->size();
             }
 
             case value_t::object:
             {
                 // delegate call to object_t::size()
-                assert(m_value.object != nullptr);
                 return m_value.object->size();
             }
 
@@ -4890,14 +4947,12 @@
             case value_t::array:
             {
                 // delegate call to array_t::max_size()
-                assert(m_value.array != nullptr);
                 return m_value.array->max_size();
             }
 
             case value_t::object:
             {
                 // delegate call to object_t::max_size()
-                assert(m_value.object != nullptr);
                 return m_value.object->max_size();
             }
 
@@ -4974,21 +5029,18 @@
 
             case value_t::string:
             {
-                assert(m_value.string != nullptr);
                 m_value.string->clear();
                 break;
             }
 
             case value_t::array:
             {
-                assert(m_value.array != nullptr);
                 m_value.array->clear();
                 break;
             }
 
             case value_t::object:
             {
-                assert(m_value.object != nullptr);
                 m_value.object->clear();
                 break;
             }
@@ -5033,10 +5085,10 @@
         {
             m_type = value_t::array;
             m_value = value_t::array;
+            assert_invariant();
         }
 
         // add element to array (move semantics)
-        assert(m_value.array != nullptr);
         m_value.array->push_back(std::move(val));
         // invalidate object
         val.m_type = value_t::null;
@@ -5069,10 +5121,10 @@
         {
             m_type = value_t::array;
             m_value = value_t::array;
+            assert_invariant();
         }
 
         // add element to array
-        assert(m_value.array != nullptr);
         m_value.array->push_back(val);
     }
 
@@ -5119,10 +5171,10 @@
         {
             m_type = value_t::object;
             m_value = value_t::object;
+            assert_invariant();
         }
 
         // add element to array
-        assert(m_value.object != nullptr);
         m_value.object->insert(val);
     }
 
@@ -5219,7 +5271,6 @@
 
             // insert to array and return iterator
             iterator result(this);
-            assert(m_value.array != nullptr);
             result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
             return result;
         }
@@ -5275,7 +5326,6 @@
 
             // insert to array and return iterator
             iterator result(this);
-            assert(m_value.array != nullptr);
             result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
             return result;
         }
@@ -5343,7 +5393,6 @@
 
         // insert to array and return iterator
         iterator result(this);
-        assert(m_value.array != nullptr);
         result.m_it.array_iterator = m_value.array->insert(
                                          pos.m_it.array_iterator,
                                          first.m_it.array_iterator,
@@ -5391,7 +5440,6 @@
 
         // insert to array and return iterator
         iterator result(this);
-        assert(m_value.array != nullptr);
         result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist);
         return result;
     }
@@ -5422,6 +5470,7 @@
     {
         std::swap(m_type, other.m_type);
         std::swap(m_value, other.m_value);
+        assert_invariant();
     }
 
     /*!
@@ -5449,7 +5498,6 @@
         // swap only works for arrays
         if (is_array())
         {
-            assert(m_value.array != nullptr);
             std::swap(*(m_value.array), other);
         }
         else
@@ -5483,7 +5531,6 @@
         // swap only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             std::swap(*(m_value.object), other);
         }
         else
@@ -5517,7 +5564,6 @@
         // swap only works for strings
         if (is_string())
         {
-            assert(m_value.string != nullptr);
             std::swap(*(m_value.string), other);
         }
         else
@@ -5604,14 +5650,10 @@
             {
                 case value_t::array:
                 {
-                    assert(lhs.m_value.array != nullptr);
-                    assert(rhs.m_value.array != nullptr);
                     return *lhs.m_value.array == *rhs.m_value.array;
                 }
                 case value_t::object:
                 {
-                    assert(lhs.m_value.object != nullptr);
-                    assert(rhs.m_value.object != nullptr);
                     return *lhs.m_value.object == *rhs.m_value.object;
                 }
                 case value_t::null:
@@ -5620,8 +5662,6 @@
                 }
                 case value_t::string:
                 {
-                    assert(lhs.m_value.string != nullptr);
-                    assert(rhs.m_value.string != nullptr);
                     return *lhs.m_value.string == *rhs.m_value.string;
                 }
                 case value_t::boolean:
@@ -5794,14 +5834,10 @@
             {
                 case value_t::array:
                 {
-                    assert(lhs.m_value.array != nullptr);
-                    assert(rhs.m_value.array != nullptr);
                     return *lhs.m_value.array < *rhs.m_value.array;
                 }
                 case value_t::object:
                 {
-                    assert(lhs.m_value.object != nullptr);
-                    assert(rhs.m_value.object != nullptr);
                     return *lhs.m_value.object < *rhs.m_value.object;
                 }
                 case value_t::null:
@@ -5810,8 +5846,6 @@
                 }
                 case value_t::string:
                 {
-                    assert(lhs.m_value.string != nullptr);
-                    assert(rhs.m_value.string != nullptr);
                     return *lhs.m_value.string < *rhs.m_value.string;
                 }
                 case value_t::boolean:
@@ -6361,8 +6395,6 @@
         {
             case value_t::object:
             {
-                assert(m_value.object != nullptr);
-
                 if (m_value.object->empty())
                 {
                     o << "{}";
@@ -6403,8 +6435,6 @@
 
             case value_t::array:
             {
-                assert(m_value.array != nullptr);
-
                 if (m_value.array->empty())
                 {
                     o << "[]";
@@ -6443,7 +6473,6 @@
 
             case value_t::string:
             {
-                assert(m_value.string != nullptr);
                 o << string_t("\"") << escape_string(*m_value.string) << "\"";
                 return;
             }
@@ -6692,6 +6721,12 @@
     This class implements a const iterator for the @ref basic_json class. From
     this class, the @ref iterator class is derived.
 
+    @note An iterator is called *initialized* when a pointer to a JSON value
+          has been set (e.g., by a constructor or a copy assignment). If the
+          iterator is default-constructed, it is *uninitialized* and most
+          methods are undefined. The library uses assertions to detect calls
+          on uninitialized iterators.
+
     @requirement The class satisfies the following concept requirements:
     - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
       The iterator that can be moved to point (forward and backward) to any
@@ -6719,7 +6754,12 @@
         /// default constructor
         const_iterator() = default;
 
-        /// constructor for a given JSON instance
+        /*!
+        @brief constructor for a given JSON instance
+        @param[in] object  pointer to a JSON object for this iterator
+        @pre object != nullptr
+        @post The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         explicit const_iterator(pointer object) noexcept
             : m_object(object)
         {
@@ -6747,40 +6787,53 @@
             }
         }
 
-        /// copy constructor given a nonconst iterator
+        /*!
+        @brief copy constructor given a non-const iterator
+        @param[in] other  iterator to copy from
+        @note It is not checked whether @a other is initialized.
+        */
         explicit const_iterator(const iterator& other) noexcept
             : m_object(other.m_object)
         {
-            assert(m_object != nullptr);
-
-            switch (m_object->m_type)
+            if (m_object != nullptr)
             {
-                case basic_json::value_t::object:
+                switch (m_object->m_type)
                 {
-                    m_it.object_iterator = other.m_it.object_iterator;
-                    break;
-                }
+                    case basic_json::value_t::object:
+                    {
+                        m_it.object_iterator = other.m_it.object_iterator;
+                        break;
+                    }
 
-                case basic_json::value_t::array:
-                {
-                    m_it.array_iterator = other.m_it.array_iterator;
-                    break;
-                }
+                    case basic_json::value_t::array:
+                    {
+                        m_it.array_iterator = other.m_it.array_iterator;
+                        break;
+                    }
 
-                default:
-                {
-                    m_it.primitive_iterator = other.m_it.primitive_iterator;
-                    break;
+                    default:
+                    {
+                        m_it.primitive_iterator = other.m_it.primitive_iterator;
+                        break;
+                    }
                 }
             }
         }
 
-        /// copy constructor
+        /*!
+        @brief copy constructor
+        @param[in] other  iterator to copy from
+        @note It is not checked whether @a other is initialized.
+        */
         const_iterator(const const_iterator& other) noexcept
             : m_object(other.m_object), m_it(other.m_it)
         {}
 
-        /// copy assignment
+        /*!
+        @brief copy assignment
+        @param[in,out] other  iterator to copy from
+        @note It is not checked whether @a other is initialized.
+        */
         const_iterator& operator=(const_iterator other) noexcept(
             std::is_nothrow_move_constructible<pointer>::value and
             std::is_nothrow_move_assignable<pointer>::value and
@@ -6794,7 +6847,10 @@
         }
 
       private:
-        /// set the iterator to the first value
+        /*!
+        @brief set the iterator to the first value
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         void set_begin() noexcept
         {
             assert(m_object != nullptr);
@@ -6803,14 +6859,12 @@
             {
                 case basic_json::value_t::object:
                 {
-                    assert(m_object->m_value.object != nullptr);
                     m_it.object_iterator = m_object->m_value.object->begin();
                     break;
                 }
 
                 case basic_json::value_t::array:
                 {
-                    assert(m_object->m_value.array != nullptr);
                     m_it.array_iterator = m_object->m_value.array->begin();
                     break;
                 }
@@ -6830,7 +6884,10 @@
             }
         }
 
-        /// set the iterator past the last value
+        /*!
+        @brief set the iterator past the last value
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         void set_end() noexcept
         {
             assert(m_object != nullptr);
@@ -6839,14 +6896,12 @@
             {
                 case basic_json::value_t::object:
                 {
-                    assert(m_object->m_value.object != nullptr);
                     m_it.object_iterator = m_object->m_value.object->end();
                     break;
                 }
 
                 case basic_json::value_t::array:
                 {
-                    assert(m_object->m_value.array != nullptr);
                     m_it.array_iterator = m_object->m_value.array->end();
                     break;
                 }
@@ -6860,7 +6915,10 @@
         }
 
       public:
-        /// return a reference to the value pointed to by the iterator
+        /*!
+        @brief return a reference to the value pointed to by the iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         reference operator*() const
         {
             assert(m_object != nullptr);
@@ -6869,14 +6927,12 @@
             {
                 case basic_json::value_t::object:
                 {
-                    assert(m_object->m_value.object);
                     assert(m_it.object_iterator != m_object->m_value.object->end());
                     return m_it.object_iterator->second;
                 }
 
                 case basic_json::value_t::array:
                 {
-                    assert(m_object->m_value.array);
                     assert(m_it.array_iterator != m_object->m_value.array->end());
                     return *m_it.array_iterator;
                 }
@@ -6900,7 +6956,10 @@
             }
         }
 
-        /// dereference the iterator
+        /*!
+        @brief dereference the iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         pointer operator->() const
         {
             assert(m_object != nullptr);
@@ -6909,14 +6968,12 @@
             {
                 case basic_json::value_t::object:
                 {
-                    assert(m_object->m_value.object);
                     assert(m_it.object_iterator != m_object->m_value.object->end());
                     return &(m_it.object_iterator->second);
                 }
 
                 case basic_json::value_t::array:
                 {
-                    assert(m_object->m_value.array);
                     assert(m_it.array_iterator != m_object->m_value.array->end());
                     return &*m_it.array_iterator;
                 }
@@ -6935,7 +6992,10 @@
             }
         }
 
-        /// post-increment (it++)
+        /*!
+        @brief post-increment (it++)
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator operator++(int)
         {
             auto result = *this;
@@ -6943,7 +7003,10 @@
             return result;
         }
 
-        /// pre-increment (++it)
+        /*!
+        @brief pre-increment (++it)
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator& operator++()
         {
             assert(m_object != nullptr);
@@ -6972,7 +7035,10 @@
             return *this;
         }
 
-        /// post-decrement (it--)
+        /*!
+        @brief post-decrement (it--)
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator operator--(int)
         {
             auto result = *this;
@@ -6980,7 +7046,10 @@
             return result;
         }
 
-        /// pre-decrement (--it)
+        /*!
+        @brief pre-decrement (--it)
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator& operator--()
         {
             assert(m_object != nullptr);
@@ -7009,7 +7078,10 @@
             return *this;
         }
 
-        /// comparison: equal
+        /*!
+        @brief  comparison: equal
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator==(const const_iterator& other) const
         {
             // if objects are not the same, the comparison is undefined
@@ -7039,13 +7111,19 @@
             }
         }
 
-        /// comparison: not equal
+        /*!
+        @brief  comparison: not equal
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator!=(const const_iterator& other) const
         {
             return not operator==(other);
         }
 
-        /// comparison: smaller
+        /*!
+        @brief  comparison: smaller
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator<(const const_iterator& other) const
         {
             // if objects are not the same, the comparison is undefined
@@ -7075,25 +7153,37 @@
             }
         }
 
-        /// comparison: less than or equal
+        /*!
+        @brief  comparison: less than or equal
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator<=(const const_iterator& other) const
         {
             return not other.operator < (*this);
         }
 
-        /// comparison: greater than
+        /*!
+        @brief  comparison: greater than
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator>(const const_iterator& other) const
         {
             return not operator<=(other);
         }
 
-        /// comparison: greater than or equal
+        /*!
+        @brief  comparison: greater than or equal
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator>=(const const_iterator& other) const
         {
             return not operator<(other);
         }
 
-        /// add to iterator
+        /*!
+        @brief  add to iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator& operator+=(difference_type i)
         {
             assert(m_object != nullptr);
@@ -7121,13 +7211,19 @@
             return *this;
         }
 
-        /// subtract from iterator
+        /*!
+        @brief  subtract from iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator& operator-=(difference_type i)
         {
             return operator+=(-i);
         }
 
-        /// add to iterator
+        /*!
+        @brief  add to iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator operator+(difference_type i)
         {
             auto result = *this;
@@ -7135,7 +7231,10 @@
             return result;
         }
 
-        /// subtract from iterator
+        /*!
+        @brief  subtract from iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator operator-(difference_type i)
         {
             auto result = *this;
@@ -7143,7 +7242,10 @@
             return result;
         }
 
-        /// return difference
+        /*!
+        @brief  return difference
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         difference_type operator-(const const_iterator& other) const
         {
             assert(m_object != nullptr);
@@ -7167,7 +7269,10 @@
             }
         }
 
-        /// access to successor
+        /*!
+        @brief  access to successor
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         reference operator[](difference_type n) const
         {
             assert(m_object != nullptr);
@@ -7203,7 +7308,10 @@
             }
         }
 
-        /// return the key of an object iterator
+        /*!
+        @brief  return the key of an object iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         typename object_t::key_type key() const
         {
             assert(m_object != nullptr);
@@ -7218,7 +7326,10 @@
             }
         }
 
-        /// return the value of an iterator
+        /*!
+        @brief  return the value of an iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         reference value() const
         {
             return operator*();
@@ -8931,6 +9042,7 @@
         basic_json parse()
         {
             basic_json result = parse_internal(true);
+            result.assert_invariant();
 
             expect(lexer::token_type::end_of_input);
 
@@ -8953,7 +9065,7 @@
                     {
                         // explicitly set result to object to cope with {}
                         result.m_type = value_t::object;
-                        result.m_value = json_value(value_t::object);
+                        result.m_value = value_t::object;
                     }
 
                     // read next token
@@ -9031,7 +9143,7 @@
                     {
                         // explicitly set result to object to cope with []
                         result.m_type = value_t::array;
-                        result.m_value = json_value(value_t::array);
+                        result.m_value = value_t::array;
                     }
 
                     // read next token
@@ -9730,7 +9842,6 @@
             basic_json result;
 
             // iterate the JSON object values
-            assert(value.m_value.object != nullptr);
             for (const auto& element : *value.m_value.object)
             {
                 if (not element.second.is_primitive())
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 7a41102..e8949d6 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++
-|  |  |__   |  |  | | | |  version 3.0.0
+|  |  |__   |  |  | | | |  version 2.0.2
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -190,6 +190,13 @@
    JSON values can be used like STL containers and provide reverse iterator
    access.
 
+@invariant The member variables @a m_value and @a m_type have the following
+relationship:
+- If `m_type == value_t::object`, then `m_value.object != nullptr`.
+- If `m_type == value_t::array`, then `m_value.array != nullptr`.
+- If `m_type == value_t::string`, then `m_value.string != nullptr`.
+The invariants are checked by member function assert_invariant().
+
 @internal
 @note ObjectType trick from http://stackoverflow.com/a/9860911
 @endinternal
@@ -985,6 +992,21 @@
         }
     };
 
+    /*!
+    @brief checks the class invariants
+
+    This function asserts the class invariants. It needs to be called at the
+    end of every constructor to make sure that created objects respect the
+    invariant. Furthermore, it has to be called each time the type of a JSON
+    value is changed, because the invariant expresses a relationship between
+    @a m_type and @a m_value.
+    */
+    void assert_invariant() const
+    {
+        assert(m_type != value_t::object or m_value.object != nullptr);
+        assert(m_type != value_t::array or m_value.array != nullptr);
+        assert(m_type != value_t::string or m_value.string != nullptr);
+    }
 
   public:
     //////////////////////////
@@ -1124,7 +1146,9 @@
     */
     basic_json(const value_t value_type)
         : m_type(value_type), m_value(value_type)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a null object (implicitly)
@@ -1132,6 +1156,9 @@
     Create a `null` JSON value. This is the implicit version of the `null`
     value constructor as it takes no parameters.
 
+    @note The class invariant is satisfied, because it poses no requirements
+    for null values.
+
     @complexity Constant.
 
     @exceptionsafety No-throw guarantee: this constructor never throws
@@ -1176,7 +1203,9 @@
     */
     basic_json(std::nullptr_t) noexcept
         : basic_json(value_t::null)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an object (explicit)
@@ -1199,7 +1228,9 @@
     */
     basic_json(const object_t& val)
         : m_type(value_t::object), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an object (implicit)
@@ -1238,6 +1269,7 @@
         using std::begin;
         using std::end;
         m_value.object = create<object_t>(begin(val), end(val));
+        assert_invariant();
     }
 
     /*!
@@ -1261,7 +1293,9 @@
     */
     basic_json(const array_t& val)
         : m_type(value_t::array), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an array (implicit)
@@ -1305,6 +1339,7 @@
         using std::begin;
         using std::end;
         m_value.array = create<array_t>(begin(val), end(val));
+        assert_invariant();
     }
 
     /*!
@@ -1330,7 +1365,9 @@
     */
     basic_json(const string_t& val)
         : m_type(value_t::string), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a string (explicit)
@@ -1354,7 +1391,9 @@
     */
     basic_json(const typename string_t::value_type* val)
         : basic_json(string_t(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a string (implicit)
@@ -1385,7 +1424,9 @@
               = 0>
     basic_json(const CompatibleStringType& val)
         : basic_json(string_t(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a boolean (explicit)
@@ -1403,7 +1444,9 @@
     */
     basic_json(boolean_t val) noexcept
         : m_type(value_t::boolean), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an integer number (explicit)
@@ -1436,7 +1479,9 @@
              = 0>
     basic_json(const number_integer_t val) noexcept
         : m_type(value_t::number_integer), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an integer number from an enum type (explicit)
@@ -1466,7 +1511,9 @@
     basic_json(const int val) noexcept
         : m_type(value_t::number_integer),
           m_value(static_cast<number_integer_t>(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an integer number (implicit)
@@ -1503,7 +1550,9 @@
     basic_json(const CompatibleNumberIntegerType val) noexcept
         : m_type(value_t::number_integer),
           m_value(static_cast<number_integer_t>(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an unsigned integer number (explicit)
@@ -1530,7 +1579,9 @@
              = 0>
     basic_json(const number_unsigned_t val) noexcept
         : m_type(value_t::number_unsigned), m_value(val)
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create an unsigned number (implicit)
@@ -1562,7 +1613,9 @@
     basic_json(const CompatibleNumberUnsignedType val) noexcept
         : m_type(value_t::number_unsigned),
           m_value(static_cast<number_unsigned_t>(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a floating-point number (explicit)
@@ -1597,6 +1650,8 @@
             m_type = value_t::null;
             m_value = json_value();
         }
+
+        assert_invariant();
     }
 
     /*!
@@ -1637,7 +1692,9 @@
              >
     basic_json(const CompatibleNumberFloatType val) noexcept
         : basic_json(number_float_t(val))
-    {}
+    {
+        assert_invariant();
+    }
 
     /*!
     @brief create a container (array or object) from an initializer list
@@ -1742,8 +1799,6 @@
             m_type = value_t::object;
             m_value = value_t::object;
 
-            assert(m_value.object != nullptr);
-
             std::for_each(init.begin(), init.end(), [this](const basic_json & element)
             {
                 m_value.object->emplace(*(element[0].m_value.string), element[1]);
@@ -1755,6 +1810,8 @@
             m_type = value_t::array;
             m_value.array = create<array_t>(init);
         }
+
+        assert_invariant();
     }
 
     /*!
@@ -1859,6 +1916,7 @@
         : m_type(value_t::array)
     {
         m_value.array = create<array_t>(cnt, val);
+        assert_invariant();
     }
 
     /*!
@@ -1879,12 +1937,14 @@
     @param[in] first begin of the range to copy from (included)
     @param[in] last end of the range to copy from (excluded)
 
+    @pre Iterators @a first and @a last must be initialized.
+
     @throw invalid_iterator (101) if iterators are not compatible; that is, do
     not belong to the same JSON value; example: `"iterators are not
     compatible"`
-    @throw invalid_iterator (104) if iterators are for a primitive type (number,
-    boolean, or string) where an out of range error can be detected easily;
-    example: `"iterators out of range"`
+    @throw invalid_iterator (104) if iterators are for a primitive type
+    (number, boolean, or string) where an out of range error can be detected
+    easily; example: `"iterators out of range"`
     @throw std::bad_alloc if allocation for object, array, or string fails
     @throw invalid_iterator (106) if called with a null value; example: `"cannot
     construct with iterators from null"`
@@ -1902,14 +1962,20 @@
                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value
                   , int>::type
               = 0>
-    basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type)
+    basic_json(InputIT first, InputIT last)
     {
+        assert(first.m_object != nullptr);
+        assert(last.m_object != nullptr);
+
         // make sure iterator fits the current value
         if (first.m_object != last.m_object)
         {
             throw invalid_iterator(101, "iterators are not compatible");
         }
 
+        // copy type from first iterator
+        m_type = first.m_object->m_type;
+
         // check if iterator range is complete for primitive values
         switch (m_type)
         {
@@ -1936,35 +2002,30 @@
         {
             case value_t::number_integer:
             {
-                assert(first.m_object != nullptr);
                 m_value.number_integer = first.m_object->m_value.number_integer;
                 break;
             }
 
             case value_t::number_unsigned:
             {
-                assert(first.m_object != nullptr);
                 m_value.number_unsigned = first.m_object->m_value.number_unsigned;
                 break;
             }
 
             case value_t::number_float:
             {
-                assert(first.m_object != nullptr);
                 m_value.number_float = first.m_object->m_value.number_float;
                 break;
             }
 
             case value_t::boolean:
             {
-                assert(first.m_object != nullptr);
                 m_value.boolean = first.m_object->m_value.boolean;
                 break;
             }
 
             case value_t::string:
             {
-                assert(first.m_object != nullptr);
                 m_value = *first.m_object->m_value.string;
                 break;
             }
@@ -1988,6 +2049,8 @@
                                        first.m_object->type_name());
             }
         }
+
+        assert_invariant();
     }
 
     /*!
@@ -2013,6 +2076,7 @@
     explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)
     {
         *this = parser(i, cb).parse();
+        assert_invariant();
     }
 
     ///////////////////////////////////////
@@ -2044,25 +2108,25 @@
     basic_json(const basic_json& other)
         : m_type(other.m_type)
     {
+        // check of passed value is valid
+        other.assert_invariant();
+
         switch (m_type)
         {
             case value_t::object:
             {
-                assert(other.m_value.object != nullptr);
                 m_value = *other.m_value.object;
                 break;
             }
 
             case value_t::array:
             {
-                assert(other.m_value.array != nullptr);
                 m_value = *other.m_value.array;
                 break;
             }
 
             case value_t::string:
             {
-                assert(other.m_value.string != nullptr);
                 m_value = *other.m_value.string;
                 break;
             }
@@ -2096,6 +2160,8 @@
                 break;
             }
         }
+
+        assert_invariant();
     }
 
     /*!
@@ -2120,9 +2186,14 @@
         : m_type(std::move(other.m_type)),
           m_value(std::move(other.m_value))
     {
+        // check that passed value is valid
+        other.assert_invariant();
+
         // invalidate payload
         other.m_type = value_t::null;
         other.m_value = {};
+
+        assert_invariant();
     }
 
     /*!
@@ -2155,9 +2226,14 @@
         std::is_nothrow_move_assignable<json_value>::value
     )
     {
+        // check that passed value is valid
+        other.assert_invariant();
+
         using std::swap;
         swap(m_type, other.m_type);
         swap(m_value, other.m_value);
+
+        assert_invariant();
         return *this;
     }
 
@@ -2178,6 +2254,8 @@
     */
     ~basic_json()
     {
+        assert_invariant();
+
         switch (m_type)
         {
             case value_t::object:
@@ -2670,7 +2748,6 @@
     {
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return T(m_value.object->begin(), m_value.object->end());
         }
         else
@@ -2684,7 +2761,6 @@
     {
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return *(m_value.object);
         }
         else
@@ -2707,7 +2783,6 @@
         if (is_array())
         {
             T to_vector;
-            assert(m_value.array != nullptr);
             std::transform(m_value.array->begin(), m_value.array->end(),
                            std::inserter(to_vector, to_vector.end()), [](basic_json i)
             {
@@ -2732,7 +2807,6 @@
         if (is_array())
         {
             std::vector<T> to_vector;
-            assert(m_value.array != nullptr);
             to_vector.reserve(m_value.array->size());
             std::transform(m_value.array->begin(), m_value.array->end(),
                            std::inserter(to_vector, to_vector.end()), [](basic_json i)
@@ -2757,7 +2831,6 @@
     {
         if (is_array())
         {
-            assert(m_value.array != nullptr);
             return T(m_value.array->begin(), m_value.array->end());
         }
         else
@@ -2771,7 +2844,6 @@
     {
         if (is_array())
         {
-            assert(m_value.array != nullptr);
             return *(m_value.array);
         }
         else
@@ -2789,7 +2861,6 @@
     {
         if (is_string())
         {
-            assert(m_value.string != nullptr);
             return *m_value.string;
         }
         else
@@ -3266,7 +3337,6 @@
         {
             try
             {
-                assert(m_value.array != nullptr);
                 return m_value.array->at(idx);
             }
             catch (std::out_of_range&)
@@ -3310,7 +3380,6 @@
         {
             try
             {
-                assert(m_value.array != nullptr);
                 return m_value.array->at(idx);
             }
             catch (std::out_of_range&)
@@ -3358,7 +3427,6 @@
         {
             try
             {
-                assert(m_value.object != nullptr);
                 return m_value.object->at(key);
             }
             catch (std::out_of_range&)
@@ -3406,7 +3474,6 @@
         {
             try
             {
-                assert(m_value.object != nullptr);
                 return m_value.object->at(key);
             }
             catch (std::out_of_range&)
@@ -3453,13 +3520,13 @@
         {
             m_type = value_t::array;
             m_value.array = create<array_t>();
+            assert_invariant();
         }
 
         // operator[] only works for arrays
         if (is_array())
         {
             // fill up array with null values if given idx is outside range
-            assert(m_value.array != nullptr);
             if (idx >= m_value.array->size())
             {
                 m_value.array->insert(m_value.array->end(),
@@ -3499,7 +3566,6 @@
         // const operator[] only works for arrays
         if (is_array())
         {
-            assert(m_value.array != nullptr);
             return m_value.array->operator[](idx);
         }
         else
@@ -3542,12 +3608,12 @@
         {
             m_type = value_t::object;
             m_value.object = create<object_t>();
+            assert_invariant();
         }
 
         // operator[] only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return m_value.object->operator[](key);
         }
         else
@@ -3588,7 +3654,6 @@
         // const operator[] only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             assert(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
@@ -3701,12 +3766,12 @@
         {
             m_type = value_t::object;
             m_value = value_t::object;
+            assert_invariant();
         }
 
         // at only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return m_value.object->operator[](key);
         }
         else
@@ -3748,7 +3813,6 @@
         // at only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             assert(m_value.object->find(key) != m_value.object->end());
             return m_value.object->find(key)->second;
         }
@@ -4076,24 +4140,25 @@
 
                 if (is_string())
                 {
-                    delete m_value.string;
+                    AllocatorType<string_t> alloc;
+                    alloc.destroy(m_value.string);
+                    alloc.deallocate(m_value.string, 1);
                     m_value.string = nullptr;
                 }
 
                 m_type = value_t::null;
+                assert_invariant();
                 break;
             }
 
             case value_t::object:
             {
-                assert(m_value.object != nullptr);
                 result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
                 break;
             }
 
             case value_t::array:
             {
-                assert(m_value.array != nullptr);
                 result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
                 break;
             }
@@ -4184,17 +4249,19 @@
 
                 if (is_string())
                 {
-                    delete m_value.string;
+                    AllocatorType<string_t> alloc;
+                    alloc.destroy(m_value.string);
+                    alloc.deallocate(m_value.string, 1);
                     m_value.string = nullptr;
                 }
 
                 m_type = value_t::null;
+                assert_invariant();
                 break;
             }
 
             case value_t::object:
             {
-                assert(m_value.object != nullptr);
                 result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
                                               last.m_it.object_iterator);
                 break;
@@ -4202,7 +4269,6 @@
 
             case value_t::array:
             {
-                assert(m_value.array != nullptr);
                 result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
                                              last.m_it.array_iterator);
                 break;
@@ -4251,7 +4317,6 @@
         // this erase only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             return m_value.object->erase(key);
         }
         else
@@ -4294,7 +4359,6 @@
                 throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
             }
 
-            assert(m_value.array != nullptr);
             m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
         }
         else
@@ -4337,7 +4401,6 @@
 
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             result.m_it.object_iterator = m_value.object->find(key);
         }
 
@@ -4354,7 +4417,6 @@
 
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             result.m_it.object_iterator = m_value.object->find(key);
         }
 
@@ -4382,7 +4444,6 @@
     size_type count(typename object_t::key_type key) const
     {
         // return 0 for all nonobject types
-        assert(not is_object() or m_value.object != nullptr);
         return is_object() ? m_value.object->count(key) : 0;
     }
 
@@ -4758,14 +4819,12 @@
             case value_t::array:
             {
                 // delegate call to array_t::empty()
-                assert(m_value.array != nullptr);
                 return m_value.array->empty();
             }
 
             case value_t::object:
             {
                 // delegate call to object_t::empty()
-                assert(m_value.object != nullptr);
                 return m_value.object->empty();
             }
 
@@ -4828,14 +4887,12 @@
             case value_t::array:
             {
                 // delegate call to array_t::size()
-                assert(m_value.array != nullptr);
                 return m_value.array->size();
             }
 
             case value_t::object:
             {
                 // delegate call to object_t::size()
-                assert(m_value.object != nullptr);
                 return m_value.object->size();
             }
 
@@ -4890,14 +4947,12 @@
             case value_t::array:
             {
                 // delegate call to array_t::max_size()
-                assert(m_value.array != nullptr);
                 return m_value.array->max_size();
             }
 
             case value_t::object:
             {
                 // delegate call to object_t::max_size()
-                assert(m_value.object != nullptr);
                 return m_value.object->max_size();
             }
 
@@ -4974,21 +5029,18 @@
 
             case value_t::string:
             {
-                assert(m_value.string != nullptr);
                 m_value.string->clear();
                 break;
             }
 
             case value_t::array:
             {
-                assert(m_value.array != nullptr);
                 m_value.array->clear();
                 break;
             }
 
             case value_t::object:
             {
-                assert(m_value.object != nullptr);
                 m_value.object->clear();
                 break;
             }
@@ -5033,10 +5085,10 @@
         {
             m_type = value_t::array;
             m_value = value_t::array;
+            assert_invariant();
         }
 
         // add element to array (move semantics)
-        assert(m_value.array != nullptr);
         m_value.array->push_back(std::move(val));
         // invalidate object
         val.m_type = value_t::null;
@@ -5069,10 +5121,10 @@
         {
             m_type = value_t::array;
             m_value = value_t::array;
+            assert_invariant();
         }
 
         // add element to array
-        assert(m_value.array != nullptr);
         m_value.array->push_back(val);
     }
 
@@ -5119,10 +5171,10 @@
         {
             m_type = value_t::object;
             m_value = value_t::object;
+            assert_invariant();
         }
 
         // add element to array
-        assert(m_value.object != nullptr);
         m_value.object->insert(val);
     }
 
@@ -5219,7 +5271,6 @@
 
             // insert to array and return iterator
             iterator result(this);
-            assert(m_value.array != nullptr);
             result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
             return result;
         }
@@ -5275,7 +5326,6 @@
 
             // insert to array and return iterator
             iterator result(this);
-            assert(m_value.array != nullptr);
             result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
             return result;
         }
@@ -5343,7 +5393,6 @@
 
         // insert to array and return iterator
         iterator result(this);
-        assert(m_value.array != nullptr);
         result.m_it.array_iterator = m_value.array->insert(
                                          pos.m_it.array_iterator,
                                          first.m_it.array_iterator,
@@ -5391,7 +5440,6 @@
 
         // insert to array and return iterator
         iterator result(this);
-        assert(m_value.array != nullptr);
         result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist);
         return result;
     }
@@ -5422,6 +5470,7 @@
     {
         std::swap(m_type, other.m_type);
         std::swap(m_value, other.m_value);
+        assert_invariant();
     }
 
     /*!
@@ -5449,7 +5498,6 @@
         // swap only works for arrays
         if (is_array())
         {
-            assert(m_value.array != nullptr);
             std::swap(*(m_value.array), other);
         }
         else
@@ -5483,7 +5531,6 @@
         // swap only works for objects
         if (is_object())
         {
-            assert(m_value.object != nullptr);
             std::swap(*(m_value.object), other);
         }
         else
@@ -5517,7 +5564,6 @@
         // swap only works for strings
         if (is_string())
         {
-            assert(m_value.string != nullptr);
             std::swap(*(m_value.string), other);
         }
         else
@@ -5604,14 +5650,10 @@
             {
                 case value_t::array:
                 {
-                    assert(lhs.m_value.array != nullptr);
-                    assert(rhs.m_value.array != nullptr);
                     return *lhs.m_value.array == *rhs.m_value.array;
                 }
                 case value_t::object:
                 {
-                    assert(lhs.m_value.object != nullptr);
-                    assert(rhs.m_value.object != nullptr);
                     return *lhs.m_value.object == *rhs.m_value.object;
                 }
                 case value_t::null:
@@ -5620,8 +5662,6 @@
                 }
                 case value_t::string:
                 {
-                    assert(lhs.m_value.string != nullptr);
-                    assert(rhs.m_value.string != nullptr);
                     return *lhs.m_value.string == *rhs.m_value.string;
                 }
                 case value_t::boolean:
@@ -5794,14 +5834,10 @@
             {
                 case value_t::array:
                 {
-                    assert(lhs.m_value.array != nullptr);
-                    assert(rhs.m_value.array != nullptr);
                     return *lhs.m_value.array < *rhs.m_value.array;
                 }
                 case value_t::object:
                 {
-                    assert(lhs.m_value.object != nullptr);
-                    assert(rhs.m_value.object != nullptr);
                     return *lhs.m_value.object < *rhs.m_value.object;
                 }
                 case value_t::null:
@@ -5810,8 +5846,6 @@
                 }
                 case value_t::string:
                 {
-                    assert(lhs.m_value.string != nullptr);
-                    assert(rhs.m_value.string != nullptr);
                     return *lhs.m_value.string < *rhs.m_value.string;
                 }
                 case value_t::boolean:
@@ -6361,8 +6395,6 @@
         {
             case value_t::object:
             {
-                assert(m_value.object != nullptr);
-
                 if (m_value.object->empty())
                 {
                     o << "{}";
@@ -6403,8 +6435,6 @@
 
             case value_t::array:
             {
-                assert(m_value.array != nullptr);
-
                 if (m_value.array->empty())
                 {
                     o << "[]";
@@ -6443,7 +6473,6 @@
 
             case value_t::string:
             {
-                assert(m_value.string != nullptr);
                 o << string_t("\"") << escape_string(*m_value.string) << "\"";
                 return;
             }
@@ -6692,6 +6721,12 @@
     This class implements a const iterator for the @ref basic_json class. From
     this class, the @ref iterator class is derived.
 
+    @note An iterator is called *initialized* when a pointer to a JSON value
+          has been set (e.g., by a constructor or a copy assignment). If the
+          iterator is default-constructed, it is *uninitialized* and most
+          methods are undefined. The library uses assertions to detect calls
+          on uninitialized iterators.
+
     @requirement The class satisfies the following concept requirements:
     - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
       The iterator that can be moved to point (forward and backward) to any
@@ -6719,7 +6754,12 @@
         /// default constructor
         const_iterator() = default;
 
-        /// constructor for a given JSON instance
+        /*!
+        @brief constructor for a given JSON instance
+        @param[in] object  pointer to a JSON object for this iterator
+        @pre object != nullptr
+        @post The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         explicit const_iterator(pointer object) noexcept
             : m_object(object)
         {
@@ -6747,40 +6787,53 @@
             }
         }
 
-        /// copy constructor given a nonconst iterator
+        /*!
+        @brief copy constructor given a non-const iterator
+        @param[in] other  iterator to copy from
+        @note It is not checked whether @a other is initialized.
+        */
         explicit const_iterator(const iterator& other) noexcept
             : m_object(other.m_object)
         {
-            assert(m_object != nullptr);
-
-            switch (m_object->m_type)
+            if (m_object != nullptr)
             {
-                case basic_json::value_t::object:
+                switch (m_object->m_type)
                 {
-                    m_it.object_iterator = other.m_it.object_iterator;
-                    break;
-                }
+                    case basic_json::value_t::object:
+                    {
+                        m_it.object_iterator = other.m_it.object_iterator;
+                        break;
+                    }
 
-                case basic_json::value_t::array:
-                {
-                    m_it.array_iterator = other.m_it.array_iterator;
-                    break;
-                }
+                    case basic_json::value_t::array:
+                    {
+                        m_it.array_iterator = other.m_it.array_iterator;
+                        break;
+                    }
 
-                default:
-                {
-                    m_it.primitive_iterator = other.m_it.primitive_iterator;
-                    break;
+                    default:
+                    {
+                        m_it.primitive_iterator = other.m_it.primitive_iterator;
+                        break;
+                    }
                 }
             }
         }
 
-        /// copy constructor
+        /*!
+        @brief copy constructor
+        @param[in] other  iterator to copy from
+        @note It is not checked whether @a other is initialized.
+        */
         const_iterator(const const_iterator& other) noexcept
             : m_object(other.m_object), m_it(other.m_it)
         {}
 
-        /// copy assignment
+        /*!
+        @brief copy assignment
+        @param[in,out] other  iterator to copy from
+        @note It is not checked whether @a other is initialized.
+        */
         const_iterator& operator=(const_iterator other) noexcept(
             std::is_nothrow_move_constructible<pointer>::value and
             std::is_nothrow_move_assignable<pointer>::value and
@@ -6794,7 +6847,10 @@
         }
 
       private:
-        /// set the iterator to the first value
+        /*!
+        @brief set the iterator to the first value
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         void set_begin() noexcept
         {
             assert(m_object != nullptr);
@@ -6803,14 +6859,12 @@
             {
                 case basic_json::value_t::object:
                 {
-                    assert(m_object->m_value.object != nullptr);
                     m_it.object_iterator = m_object->m_value.object->begin();
                     break;
                 }
 
                 case basic_json::value_t::array:
                 {
-                    assert(m_object->m_value.array != nullptr);
                     m_it.array_iterator = m_object->m_value.array->begin();
                     break;
                 }
@@ -6830,7 +6884,10 @@
             }
         }
 
-        /// set the iterator past the last value
+        /*!
+        @brief set the iterator past the last value
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         void set_end() noexcept
         {
             assert(m_object != nullptr);
@@ -6839,14 +6896,12 @@
             {
                 case basic_json::value_t::object:
                 {
-                    assert(m_object->m_value.object != nullptr);
                     m_it.object_iterator = m_object->m_value.object->end();
                     break;
                 }
 
                 case basic_json::value_t::array:
                 {
-                    assert(m_object->m_value.array != nullptr);
                     m_it.array_iterator = m_object->m_value.array->end();
                     break;
                 }
@@ -6860,7 +6915,10 @@
         }
 
       public:
-        /// return a reference to the value pointed to by the iterator
+        /*!
+        @brief return a reference to the value pointed to by the iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         reference operator*() const
         {
             assert(m_object != nullptr);
@@ -6869,14 +6927,12 @@
             {
                 case basic_json::value_t::object:
                 {
-                    assert(m_object->m_value.object);
                     assert(m_it.object_iterator != m_object->m_value.object->end());
                     return m_it.object_iterator->second;
                 }
 
                 case basic_json::value_t::array:
                 {
-                    assert(m_object->m_value.array);
                     assert(m_it.array_iterator != m_object->m_value.array->end());
                     return *m_it.array_iterator;
                 }
@@ -6900,7 +6956,10 @@
             }
         }
 
-        /// dereference the iterator
+        /*!
+        @brief dereference the iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         pointer operator->() const
         {
             assert(m_object != nullptr);
@@ -6909,14 +6968,12 @@
             {
                 case basic_json::value_t::object:
                 {
-                    assert(m_object->m_value.object);
                     assert(m_it.object_iterator != m_object->m_value.object->end());
                     return &(m_it.object_iterator->second);
                 }
 
                 case basic_json::value_t::array:
                 {
-                    assert(m_object->m_value.array);
                     assert(m_it.array_iterator != m_object->m_value.array->end());
                     return &*m_it.array_iterator;
                 }
@@ -6935,7 +6992,10 @@
             }
         }
 
-        /// post-increment (it++)
+        /*!
+        @brief post-increment (it++)
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator operator++(int)
         {
             auto result = *this;
@@ -6943,7 +7003,10 @@
             return result;
         }
 
-        /// pre-increment (++it)
+        /*!
+        @brief pre-increment (++it)
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator& operator++()
         {
             assert(m_object != nullptr);
@@ -6972,7 +7035,10 @@
             return *this;
         }
 
-        /// post-decrement (it--)
+        /*!
+        @brief post-decrement (it--)
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator operator--(int)
         {
             auto result = *this;
@@ -6980,7 +7046,10 @@
             return result;
         }
 
-        /// pre-decrement (--it)
+        /*!
+        @brief pre-decrement (--it)
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator& operator--()
         {
             assert(m_object != nullptr);
@@ -7009,7 +7078,10 @@
             return *this;
         }
 
-        /// comparison: equal
+        /*!
+        @brief  comparison: equal
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator==(const const_iterator& other) const
         {
             // if objects are not the same, the comparison is undefined
@@ -7039,13 +7111,19 @@
             }
         }
 
-        /// comparison: not equal
+        /*!
+        @brief  comparison: not equal
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator!=(const const_iterator& other) const
         {
             return not operator==(other);
         }
 
-        /// comparison: smaller
+        /*!
+        @brief  comparison: smaller
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator<(const const_iterator& other) const
         {
             // if objects are not the same, the comparison is undefined
@@ -7075,25 +7153,37 @@
             }
         }
 
-        /// comparison: less than or equal
+        /*!
+        @brief  comparison: less than or equal
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator<=(const const_iterator& other) const
         {
             return not other.operator < (*this);
         }
 
-        /// comparison: greater than
+        /*!
+        @brief  comparison: greater than
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator>(const const_iterator& other) const
         {
             return not operator<=(other);
         }
 
-        /// comparison: greater than or equal
+        /*!
+        @brief  comparison: greater than or equal
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         bool operator>=(const const_iterator& other) const
         {
             return not operator<(other);
         }
 
-        /// add to iterator
+        /*!
+        @brief  add to iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator& operator+=(difference_type i)
         {
             assert(m_object != nullptr);
@@ -7121,13 +7211,19 @@
             return *this;
         }
 
-        /// subtract from iterator
+        /*!
+        @brief  subtract from iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator& operator-=(difference_type i)
         {
             return operator+=(-i);
         }
 
-        /// add to iterator
+        /*!
+        @brief  add to iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator operator+(difference_type i)
         {
             auto result = *this;
@@ -7135,7 +7231,10 @@
             return result;
         }
 
-        /// subtract from iterator
+        /*!
+        @brief  subtract from iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         const_iterator operator-(difference_type i)
         {
             auto result = *this;
@@ -7143,7 +7242,10 @@
             return result;
         }
 
-        /// return difference
+        /*!
+        @brief  return difference
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         difference_type operator-(const const_iterator& other) const
         {
             assert(m_object != nullptr);
@@ -7167,7 +7269,10 @@
             }
         }
 
-        /// access to successor
+        /*!
+        @brief  access to successor
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         reference operator[](difference_type n) const
         {
             assert(m_object != nullptr);
@@ -7203,7 +7308,10 @@
             }
         }
 
-        /// return the key of an object iterator
+        /*!
+        @brief  return the key of an object iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         typename object_t::key_type key() const
         {
             assert(m_object != nullptr);
@@ -7218,7 +7326,10 @@
             }
         }
 
-        /// return the value of an iterator
+        /*!
+        @brief  return the value of an iterator
+        @pre The iterator is initialized; i.e. `m_object != nullptr`.
+        */
         reference value() const
         {
             return operator*();
@@ -8228,6 +8339,7 @@
         basic_json parse()
         {
             basic_json result = parse_internal(true);
+            result.assert_invariant();
 
             expect(lexer::token_type::end_of_input);
 
@@ -8250,7 +8362,7 @@
                     {
                         // explicitly set result to object to cope with {}
                         result.m_type = value_t::object;
-                        result.m_value = json_value(value_t::object);
+                        result.m_value = value_t::object;
                     }
 
                     // read next token
@@ -8328,7 +8440,7 @@
                     {
                         // explicitly set result to object to cope with []
                         result.m_type = value_t::array;
-                        result.m_value = json_value(value_t::array);
+                        result.m_value = value_t::array;
                     }
 
                     // read next token
@@ -9027,7 +9139,6 @@
             basic_json result;
 
             // iterate the JSON object values
-            assert(value.m_value.object != nullptr);
             for (const auto& element : *value.m_value.object)
             {
                 if (not element.second.is_primitive())
diff --git a/test/src/fuzz.cpp b/test/src/fuzz.cpp
index c203845..f40059d 100644
--- a/test/src/fuzz.cpp
+++ b/test/src/fuzz.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (fuzz test support)
-|  |  |__   |  |  | | | |  version 3.0.0
+|  |  |__   |  |  | | | |  version 2.0.2
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Run "make fuzz_testing" and follow the instructions.
diff --git a/test/src/unit.cpp b/test/src/unit.cpp
index b7688a4..75f689f 100644
--- a/test/src/unit.cpp
+++ b/test/src/unit.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 3.0.0
+|  |  |__   |  |  | | | |  version 2.0.2
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.