From 20bcf1015bbd55c7a8c9663ac87929b68d3d5869 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sat, 25 Mar 2017 14:19:05 +0100
Subject: [PATCH 1/7] :memo: cleanup after #536

---
 README.md | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 3d912305..d0fdeb80 100644
--- a/README.md
+++ b/README.md
@@ -706,6 +706,7 @@ Though it's 2016 already, the support for C++11 is still a bit sparse. Currently
 - GCC 4.9 - 6.0 (and possibly later)
 - Clang 3.4 - 3.9 (and possibly later)
 - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
+- Microsoft Visual C++ 2017 / Build Tools 15.1.548.43366 (and possibly later)
 
 I would be happy to learn about other compilers/versions.
 
@@ -744,7 +745,7 @@ The following compilers are currently used in continuous integration at [Travis]
 | Clang Xcode 8.1 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
 | Clang Xcode 8.2 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
 | Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25123.0 | 
-
+| Visual Studio 2017 | Windows Server 2016 | Microsoft (R) Build Engine version 15.1.548.43366 |
 
 ## License
 
@@ -809,7 +810,7 @@ I deeply appreciate the help of the following people.
 - [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation.
 - [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`.
 - [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion.
-- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable.
+- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix.
 - [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file.
 - [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function.
 - [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](http://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing.

From a58ed3cd178c89e3ae2fafe0af7a799f6ee0c234 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sat, 25 Mar 2017 14:47:23 +0100
Subject: [PATCH 2/7] :lipstick: cleanup

---
 src/json.hpp      | 10 +++++-----
 src/json.hpp.re2c | 10 +++++-----
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 6ec045aa..cc69d542 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -12486,7 +12486,7 @@ basic_json_parser_74:
                         {
                             result = &result->operator[](static_cast<size_type>(std::stoi(reference_token)));
                         }
-                        JSON_CATCH(std::invalid_argument&)
+                        JSON_CATCH (std::invalid_argument&)
                         {
                             JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
@@ -12585,7 +12585,7 @@ basic_json_parser_74:
                             {
                                 ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
                             }
-                            JSON_CATCH(std::invalid_argument&)
+                            JSON_CATCH (std::invalid_argument&)
                             {
                                 JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                             }
@@ -12643,7 +12643,7 @@ basic_json_parser_74:
                         {
                             ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
                         }
-                        JSON_CATCH(std::invalid_argument&)
+                        JSON_CATCH (std::invalid_argument&)
                         {
                             JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
@@ -12707,7 +12707,7 @@ basic_json_parser_74:
                         {
                             ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
                         }
-                        JSON_CATCH(std::invalid_argument&)
+                        JSON_CATCH (std::invalid_argument&)
                         {
                             JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
@@ -12764,7 +12764,7 @@ basic_json_parser_74:
                         {
                             ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
                         }
-                        JSON_CATCH(std::invalid_argument&)
+                        JSON_CATCH (std::invalid_argument&)
                         {
                             JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 7d21f76b..1480a5f5 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -11519,7 +11519,7 @@ class basic_json
                         {
                             result = &result->operator[](static_cast<size_type>(std::stoi(reference_token)));
                         }
-                        JSON_CATCH(std::invalid_argument&)
+                        JSON_CATCH (std::invalid_argument&)
                         {
                             JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
@@ -11618,7 +11618,7 @@ class basic_json
                             {
                                 ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
                             }
-                            JSON_CATCH(std::invalid_argument&)
+                            JSON_CATCH (std::invalid_argument&)
                             {
                                 JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                             }
@@ -11676,7 +11676,7 @@ class basic_json
                         {
                             ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
                         }
-                        JSON_CATCH(std::invalid_argument&)
+                        JSON_CATCH (std::invalid_argument&)
                         {
                             JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
@@ -11740,7 +11740,7 @@ class basic_json
                         {
                             ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
                         }
-                        JSON_CATCH(std::invalid_argument&)
+                        JSON_CATCH (std::invalid_argument&)
                         {
                             JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
@@ -11797,7 +11797,7 @@ class basic_json
                         {
                             ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
                         }
-                        JSON_CATCH(std::invalid_argument&)
+                        JSON_CATCH (std::invalid_argument&)
                         {
                             JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
                         }

From cc36c65a8907f67519c2024b4ed29ea8ad178798 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sat, 25 Mar 2017 16:22:52 +0100
Subject: [PATCH 3/7] :ambulance: made exceptions nothrow-copy-constructible
 #531

To have nothrow-copy-constructible exceptions, we inherit from
std::runtime_error which can cope with arbitrary-length error messages.
Intermediate strings are built with static functions and then passed to
the actual constructor.
---
 README.md                  |   2 +-
 src/json.hpp               | 341 +++++++++++++++++++++----------------
 src/json.hpp.re2c          | 341 +++++++++++++++++++++----------------
 test/src/unit-noexcept.cpp |   8 +
 4 files changed, 391 insertions(+), 301 deletions(-)

diff --git a/README.md b/README.md
index d0fdeb80..e23e3c94 100644
--- a/README.md
+++ b/README.md
@@ -894,7 +894,7 @@ $ make json_unit -Ctest
 $ ./test/json_unit "*"
 
 ===============================================================================
-All tests passed (11202597 assertions in 47 test cases)
+All tests passed (11203022 assertions in 48 test cases)
 ```
 
 Alternatively, you can use [CMake](https://cmake.org) and run
diff --git a/src/json.hpp b/src/json.hpp
index cc69d542..bdada276 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -116,29 +116,34 @@ namespace detail
 
 Extension of std::exception objects with a member @a id for exception ids.
 
+@note To have nothrow-copy-constructible exceptions, we inherit from
+      std::runtime_error which can cope with arbitrary-length error messages.
+      Intermediate strings are built with static functions and then passed to
+      the actual constructor.
+
 @since version 3.0.0
 */
-class exception : public std::exception
+class exception : public std::runtime_error
 {
   public:
-    /// create exception with id an explanatory string
-    exception(int id_, const std::string& ename, const std::string& what_arg_)
-        : id(id_),
-          what_arg("[json.exception." + ename + "." + std::to_string(id_) + "] " + what_arg_)
-    {}
-
     /// returns the explanatory string
     virtual const char* what() const noexcept override
     {
-        return what_arg.c_str();
+        return std::runtime_error::what();
     }
 
     /// the id of the exception
     const int id;
 
-  private:
-    /// the explanatory string
-    const std::string what_arg;
+  protected:
+    exception(int id_, const char* what_arg)
+        : std::runtime_error(what_arg), id(id_)
+    {}
+
+    static std::string name(const std::string& ename, int id_)
+    {
+        return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
+    }
 };
 
 /*!
@@ -184,14 +189,16 @@ class parse_error : public exception
     @param[in] id_        the id of the exception
     @param[in] byte_      the byte index where the error occured (or 0 if
                           the position cannot be determined)
-    @param[in] what_arg_  the explanatory string
+    @param[in] what_arg   the explanatory string
+    @return parse_error object
     */
-    parse_error(int id_, size_t byte_, const std::string& what_arg_)
-        : exception(id_, "parse_error", "parse error" +
-                    (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
-                    ": " + what_arg_),
-          byte(byte_)
-    {}
+    static parse_error create(int id_, size_t byte_, const std::string& what_arg)
+    {
+        std::string w = exception::name("parse_error", id_) + "parse error" +
+                        (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
+                        ": " + what_arg;
+        return parse_error(id_, byte_, w.c_str());
+    }
 
     /*!
     @brief byte index of the parse error
@@ -204,6 +211,12 @@ class parse_error : public exception
           MessagePack).
     */
     const size_t byte;
+
+  private:
+    parse_error(int id_, size_t byte_, const char* what_arg)
+        : exception(id_, what_arg),
+          byte(byte_)
+    {}
 };
 
 /*!
@@ -233,8 +246,15 @@ json.exception.invalid_iterator.214 | cannot get value | Cannot get value for it
 class invalid_iterator : public exception
 {
   public:
-    invalid_iterator(int id_, const std::string& what_arg_)
-        : exception(id_, "invalid_iterator", what_arg_)
+    static invalid_iterator create(int id_, const std::string& what_arg)
+    {
+        std::string w = exception::name("invalid_iterator", id_) + what_arg;
+        return invalid_iterator(id_, w.c_str());
+    }
+
+  private:
+    invalid_iterator(int id_, const char* what_arg)
+        : exception(id_, what_arg)
     {}
 };
 
@@ -265,8 +285,15 @@ json.exception.type_error.315 | values in object must be primitive | The @ref un
 class type_error : public exception
 {
   public:
-    type_error(int id_, const std::string& what_arg_)
-        : exception(id_, "type_error", what_arg_)
+    static type_error create(int id_, const std::string& what_arg)
+    {
+        std::string w = exception::name("type_error", id_) + what_arg;
+        return type_error(id_, w.c_str());
+    }
+
+  private:
+    type_error(int id_, const char* what_arg)
+        : exception(id_, what_arg)
     {}
 };
 
@@ -289,8 +316,15 @@ json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed n
 class out_of_range : public exception
 {
   public:
-    out_of_range(int id_, const std::string& what_arg_)
-        : exception(id_, "out_of_range", what_arg_)
+    static out_of_range create(int id_, const std::string& what_arg)
+    {
+        std::string w = exception::name("out_of_range", id_) + what_arg;
+        return out_of_range(id_, w.c_str());
+    }
+
+  private:
+    out_of_range(int id_, const char* what_arg)
+        : exception(id_, what_arg)
     {}
 };
 
@@ -308,8 +342,15 @@ json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "valu
 class other_error : public exception
 {
   public:
-    other_error(int id_, const std::string& what_arg_)
-        : exception(id_, "other_error", what_arg_)
+    static other_error create(int id_, const std::string& what_arg)
+    {
+        std::string w = exception::name("other_error", id_) + what_arg;
+        return other_error(id_, w.c_str());
+    }
+
+  private:
+    other_error(int id_, const char* what_arg)
+        : exception(id_, what_arg)
     {}
 };
 
@@ -841,7 +882,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
         }
         default:
         {
-            JSON_THROW(type_error(302, "type must be number, but is " + j.type_name()));
+            JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name()));
         }
     }
 }
@@ -851,7 +892,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
 {
     if (not j.is_boolean())
     {
-        JSON_THROW(type_error(302, "type must be boolean, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be boolean, but is " + j.type_name()));
     }
     b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
 }
@@ -861,7 +902,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
 {
     if (not j.is_string())
     {
-        JSON_THROW(type_error(302, "type must be string, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be string, but is " + j.type_name()));
     }
     s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
 }
@@ -898,7 +939,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
 {
     if (not j.is_array())
     {
-        JSON_THROW(type_error(302, "type must be array, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name()));
     }
     arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
 }
@@ -910,7 +951,7 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
 {
     if (not j.is_array())
     {
-        JSON_THROW(type_error(302, "type must be array, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name()));
     }
 
     for (auto it = j.rbegin(), end = j.rend(); it != end; ++it)
@@ -961,7 +1002,7 @@ void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
 {
     if (not j.is_array())
     {
-        JSON_THROW(type_error(302, "type must be array, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name()));
     }
 
     from_json_array_impl(j, arr, priority_tag<1> {});
@@ -973,7 +1014,7 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
 {
     if (not j.is_object())
     {
-        JSON_THROW(type_error(302, "type must be object, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be object, but is " + j.type_name()));
     }
 
     auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
@@ -1023,7 +1064,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val)
         }
         default:
         {
-            JSON_THROW(type_error(302, "type must be number, but is " + j.type_name()));
+            JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name()));
         }
     }
 }
@@ -1970,7 +2011,7 @@ class basic_json
                 {
                     if (t == value_t::null)
                     {
-                        JSON_THROW(other_error(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE
+                        JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE
                     }
                     break;
                 }
@@ -2325,7 +2366,7 @@ class basic_json
             // if object is wanted but impossible, throw an exception
             if (manual_type == value_t::object and not is_an_object)
             {
-                JSON_THROW(type_error(301, "cannot create object from initializer list"));
+                JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
             }
         }
 
@@ -2509,7 +2550,7 @@ class basic_json
         // make sure iterator fits the current value
         if (first.m_object != last.m_object)
         {
-            JSON_THROW(invalid_iterator(201, "iterators are not compatible"));
+            JSON_THROW(invalid_iterator::create(201, "iterators are not compatible"));
         }
 
         // copy type from first iterator
@@ -2526,7 +2567,7 @@ class basic_json
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    JSON_THROW(invalid_iterator(204, "iterators out of range"));
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
                 }
                 break;
             }
@@ -2585,8 +2626,8 @@ class basic_json
 
             default:
             {
-                JSON_THROW(invalid_iterator(206, "cannot construct with iterators from " +
-                                            first.m_object->type_name()));
+                JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " +
+                                                    first.m_object->type_name()));
             }
         }
 
@@ -3224,7 +3265,7 @@ class basic_json
             return m_value.boolean;
         }
 
-        JSON_THROW(type_error(302, "type must be boolean, but is " + type_name()));
+        JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name()));
     }
 
     /// get a pointer to the value (object)
@@ -3336,7 +3377,7 @@ class basic_json
             return *ptr;
         }
 
-        JSON_THROW(type_error(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name()));
+        JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name()));
     }
 
   public:
@@ -3738,12 +3779,12 @@ class basic_json
             JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range"));
+                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
             }
         }
         else
         {
-            JSON_THROW(type_error(304, "cannot use at() with " + type_name()));
+            JSON_THROW(type_error::create(304, "cannot use at() with " + type_name()));
         }
     }
 
@@ -3785,12 +3826,12 @@ class basic_json
             JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range"));
+                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
             }
         }
         else
         {
-            JSON_THROW(type_error(304, "cannot use at() with " + type_name()));
+            JSON_THROW(type_error::create(304, "cannot use at() with " + type_name()));
         }
     }
 
@@ -3836,12 +3877,12 @@ class basic_json
             JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                JSON_THROW(out_of_range(403, "key '" + key + "' not found"));
+                JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
             }
         }
         else
         {
-            JSON_THROW(type_error(304, "cannot use at() with " + type_name()));
+            JSON_THROW(type_error::create(304, "cannot use at() with " + type_name()));
         }
     }
 
@@ -3887,12 +3928,12 @@ class basic_json
             JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                JSON_THROW(out_of_range(403, "key '" + key + "' not found"));
+                JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
             }
         }
         else
         {
-            JSON_THROW(type_error(304, "cannot use at() with " + type_name()));
+            JSON_THROW(type_error::create(304, "cannot use at() with " + type_name()));
         }
     }
 
@@ -3945,7 +3986,7 @@ class basic_json
             return m_value.array->operator[](idx);
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3975,7 +4016,7 @@ class basic_json
             return m_value.array->operator[](idx);
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4021,7 +4062,7 @@ class basic_json
             return m_value.object->operator[](key);
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4063,7 +4104,7 @@ class basic_json
             return m_value.object->find(key)->second;
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4178,7 +4219,7 @@ class basic_json
             return m_value.object->operator[](key);
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4221,7 +4262,7 @@ class basic_json
             return m_value.object->find(key)->second;
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4290,7 +4331,7 @@ class basic_json
         }
         else
         {
-            JSON_THROW(type_error(306, "cannot use value() with " + type_name()));
+            JSON_THROW(type_error::create(306, "cannot use value() with " + type_name()));
         }
     }
 
@@ -4362,7 +4403,7 @@ class basic_json
             }
         }
 
-        JSON_THROW(type_error(306, "cannot use value() with " + type_name()));
+        JSON_THROW(type_error::create(306, "cannot use value() with " + type_name()));
     }
 
     /*!
@@ -4515,7 +4556,7 @@ class basic_json
         // make sure iterator fits the current value
         if (this != pos.m_object)
         {
-            JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
         }
 
         IteratorType result = end();
@@ -4530,7 +4571,7 @@ class basic_json
             {
                 if (not pos.m_it.primitive_iterator.is_begin())
                 {
-                    JSON_THROW(invalid_iterator(205, "iterator out of range"));
+                    JSON_THROW(invalid_iterator::create(205, "iterator out of range"));
                 }
 
                 if (is_string())
@@ -4560,7 +4601,7 @@ class basic_json
 
             default:
             {
-                JSON_THROW(type_error(307, "cannot use erase() with " + type_name()));
+                JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name()));
             }
         }
 
@@ -4622,7 +4663,7 @@ class basic_json
         // make sure iterator fits the current value
         if (this != first.m_object or this != last.m_object)
         {
-            JSON_THROW(invalid_iterator(203, "iterators do not fit current value"));
+            JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value"));
         }
 
         IteratorType result = end();
@@ -4637,7 +4678,7 @@ class basic_json
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    JSON_THROW(invalid_iterator(204, "iterators out of range"));
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
                 }
 
                 if (is_string())
@@ -4669,7 +4710,7 @@ class basic_json
 
             default:
             {
-                JSON_THROW(type_error(307, "cannot use erase() with " + type_name()));
+                JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name()));
             }
         }
 
@@ -4713,7 +4754,7 @@ class basic_json
             return m_value.object->erase(key);
         }
 
-        JSON_THROW(type_error(307, "cannot use erase() with " + type_name()));
+        JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name()));
     }
 
     /*!
@@ -4747,14 +4788,14 @@ class basic_json
         {
             if (idx >= size())
             {
-                JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range"));
+                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
             }
 
             m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
         }
         else
         {
-            JSON_THROW(type_error(307, "cannot use erase() with " + type_name()));
+            JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name()));
         }
     }
 
@@ -5472,7 +5513,7 @@ class basic_json
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            JSON_THROW(type_error(308, "cannot use push_back() with " + type_name()));
+            JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -5508,7 +5549,7 @@ class basic_json
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            JSON_THROW(type_error(308, "cannot use push_back() with " + type_name()));
+            JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -5558,7 +5599,7 @@ class basic_json
         // push_back only works for null objects or objects
         if (not(is_null() or is_object()))
         {
-            JSON_THROW(type_error(308, "cannot use push_back() with " + type_name()));
+            JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an object
@@ -5658,7 +5699,7 @@ class basic_json
         // emplace_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            JSON_THROW(type_error(311, "cannot use emplace_back() with " + type_name()));
+            JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -5706,7 +5747,7 @@ class basic_json
         // emplace only works for null objects or arrays
         if (not(is_null() or is_object()))
         {
-            JSON_THROW(type_error(311, "cannot use emplace() with " + type_name()));
+            JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name()));
         }
 
         // transform null object into an object
@@ -5757,7 +5798,7 @@ class basic_json
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
             }
 
             // insert to array and return iterator
@@ -5766,7 +5807,7 @@ class basic_json
             return result;
         }
 
-        JSON_THROW(type_error(309, "cannot use insert() with " + type_name()));
+        JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name()));
     }
 
     /*!
@@ -5810,7 +5851,7 @@ class basic_json
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
             }
 
             // insert to array and return iterator
@@ -5819,7 +5860,7 @@ class basic_json
             return result;
         }
 
-        JSON_THROW(type_error(309, "cannot use insert() with " + type_name()));
+        JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name()));
     }
 
     /*!
@@ -5857,24 +5898,24 @@ class basic_json
         // insert only works for arrays
         if (not is_array())
         {
-            JSON_THROW(type_error(309, "cannot use insert() with " + type_name()));
+            JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name()));
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
         }
 
         // check if range iterators belong to the same JSON object
         if (first.m_object != last.m_object)
         {
-            JSON_THROW(invalid_iterator(210, "iterators do not fit"));
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
         }
 
         if (first.m_object == this or last.m_object == this)
         {
-            JSON_THROW(invalid_iterator(211, "passed iterators may not belong to container"));
+            JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
         }
 
         // insert to array and return iterator
@@ -5915,13 +5956,13 @@ class basic_json
         // insert only works for arrays
         if (not is_array())
         {
-            JSON_THROW(type_error(309, "cannot use insert() with " + type_name()));
+            JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name()));
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
         }
 
         // insert to array and return iterator
@@ -5988,7 +6029,7 @@ class basic_json
         }
         else
         {
-            JSON_THROW(type_error(310, "cannot use swap() with " + type_name()));
+            JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name()));
         }
     }
 
@@ -6021,7 +6062,7 @@ class basic_json
         }
         else
         {
-            JSON_THROW(type_error(310, "cannot use swap() with " + type_name()));
+            JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name()));
         }
     }
 
@@ -6054,7 +6095,7 @@ class basic_json
         }
         else
         {
-            JSON_THROW(type_error(310, "cannot use swap() with " + type_name()));
+            JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name()));
         }
     }
 
@@ -7999,19 +8040,19 @@ class basic_json
         // simple case: requested length is greater than the vector's length
         if (len > size or offset > size)
         {
-            JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
+            JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
         }
 
         // second case: adding offset would result in overflow
         if ((size > ((std::numeric_limits<size_t>::max)() - offset)))
         {
-            JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
+            JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
         }
 
         // last case: reading past the end of the vector
         if (len + offset > size)
         {
-            JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
+            JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
         }
     }
 
@@ -8043,7 +8084,7 @@ class basic_json
 
         std::stringstream ss;
         ss << std::hex << static_cast<int>(v[idx]);
-        JSON_THROW(parse_error(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str()));
+        JSON_THROW(parse_error::create(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str()));
     }
 
     /*!
@@ -8073,7 +8114,7 @@ class basic_json
 
         std::stringstream ss;
         ss << std::hex << static_cast<int>(v[idx]);
-        JSON_THROW(parse_error(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str()));
+        JSON_THROW(parse_error::create(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str()));
     }
 
     /*!
@@ -8316,7 +8357,7 @@ class basic_json
                 {
                     std::stringstream ss;
                     ss << std::hex << static_cast<int>(v[current_idx]);
-                    JSON_THROW(parse_error(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str()));
+                    JSON_THROW(parse_error::create(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str()));
                 }
             }
         }
@@ -8816,7 +8857,7 @@ class basic_json
             {
                 std::stringstream ss;
                 ss << std::hex << static_cast<int>(v[current_idx]);
-                JSON_THROW(parse_error(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str()));
+                JSON_THROW(parse_error::create(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str()));
             }
         }
     }
@@ -9697,7 +9738,7 @@ class basic_json
 
                 case basic_json::value_t::null:
                 {
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
 
                 default:
@@ -9707,7 +9748,7 @@ class basic_json
                         return *m_object;
                     }
 
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
             }
         }
@@ -9741,7 +9782,7 @@ class basic_json
                         return m_object;
                     }
 
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
             }
         }
@@ -9841,7 +9882,7 @@ class basic_json
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers"));
+                JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
             }
 
             assert(m_object != nullptr);
@@ -9883,7 +9924,7 @@ class basic_json
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers"));
+                JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
             }
 
             assert(m_object != nullptr);
@@ -9892,7 +9933,7 @@ class basic_json
             {
                 case basic_json::value_t::object:
                 {
-                    JSON_THROW(invalid_iterator(213, "cannot compare order of object iterators"));
+                    JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -9946,7 +9987,7 @@ class basic_json
             {
                 case basic_json::value_t::object:
                 {
-                    JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators"));
+                    JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -10008,7 +10049,7 @@ class basic_json
             {
                 case basic_json::value_t::object:
                 {
-                    JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators"));
+                    JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -10035,7 +10076,7 @@ class basic_json
             {
                 case basic_json::value_t::object:
                 {
-                    JSON_THROW(invalid_iterator(208, "cannot use operator[] for object iterators"));
+                    JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -10045,7 +10086,7 @@ class basic_json
 
                 case basic_json::value_t::null:
                 {
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
 
                 default:
@@ -10055,7 +10096,7 @@ class basic_json
                         return *m_object;
                     }
 
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
             }
         }
@@ -10073,7 +10114,7 @@ class basic_json
                 return m_it.object_iterator->first;
             }
 
-            JSON_THROW(invalid_iterator(207, "cannot use key() for non-object iterators"));
+            JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators"));
         }
 
         /*!
@@ -10263,7 +10304,7 @@ class basic_json
             // immediately abort if stream is erroneous
             if (s.fail())
             {
-                JSON_THROW(parse_error(111, 0, "bad input stream"));
+                JSON_THROW(parse_error::create(111, 0, "bad input stream"));
             }
 
             // fill buffer
@@ -10330,7 +10371,7 @@ class basic_json
                 }
                 else
                 {
-                    JSON_THROW(parse_error(102, get_position(), "missing or wrong low surrogate"));
+                    JSON_THROW(parse_error::create(102, get_position(), "missing or wrong low surrogate"));
                 }
             }
 
@@ -10364,7 +10405,7 @@ class basic_json
             }
             else
             {
-                JSON_THROW(parse_error(103, get_position(), "code points above 0x10FFFF are invalid"));
+                JSON_THROW(parse_error::create(103, get_position(), "code points above 0x10FFFF are invalid"));
             }
 
             return result;
@@ -11560,7 +11601,7 @@ basic_json_parser_74:
                 // check if stream is still good
                 if (m_stream->fail())
                 {
-                    JSON_THROW(parse_error(111, 0, "bad input stream"));
+                    JSON_THROW(parse_error::create(111, 0, "bad input stream"));
                 }
 
                 std::getline(*m_stream, m_line_buffer_tmp, '\n');
@@ -11729,7 +11770,7 @@ basic_json_parser_74:
                                 // make sure there is a subsequent unicode
                                 if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u')
                                 {
-                                    JSON_THROW(parse_error(102, get_position(), "missing low surrogate"));
+                                    JSON_THROW(parse_error::create(102, get_position(), "missing low surrogate"));
                                 }
 
                                 // get code yyyy from uxxxx\uyyyy
@@ -11742,7 +11783,7 @@ basic_json_parser_74:
                             else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF)
                             {
                                 // we found a lone low surrogate
-                                JSON_THROW(parse_error(102, get_position(), "missing high surrogate"));
+                                JSON_THROW(parse_error::create(102, get_position(), "missing high surrogate"));
                             }
                             else
                             {
@@ -11986,7 +12027,7 @@ basic_json_parser_74:
                 // throw in case of infinity or NAN
                 if (not std::isfinite(result.m_value.number_float))
                 {
-                    JSON_THROW(out_of_range(406, "number overflow parsing '" + get_token_string() + "'"));
+                    JSON_THROW(out_of_range::create(406, "number overflow parsing '" + get_token_string() + "'"));
                 }
 
                 return true;
@@ -12299,7 +12340,7 @@ basic_json_parser_74:
                               "'") :
                               lexer::token_type_name(last_token));
                 error_msg += "; expected " + lexer::token_type_name(t);
-                JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg));
+                JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
             }
         }
 
@@ -12314,7 +12355,7 @@ basic_json_parser_74:
                 error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token_string() +
                               "'") :
                               lexer::token_type_name(last_token));
-                JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg));
+                JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
             }
         }
 
@@ -12413,7 +12454,7 @@ basic_json_parser_74:
         {
             if (is_root())
             {
-                JSON_THROW(out_of_range(405, "JSON pointer has no parent"));
+                JSON_THROW(out_of_range::create(405, "JSON pointer has no parent"));
             }
 
             auto last = reference_tokens.back();
@@ -12431,7 +12472,7 @@ basic_json_parser_74:
         {
             if (is_root())
             {
-                JSON_THROW(out_of_range(405, "JSON pointer has no parent"));
+                JSON_THROW(out_of_range::create(405, "JSON pointer has no parent"));
             }
 
             json_pointer result = *this;
@@ -12488,7 +12529,7 @@ basic_json_parser_74:
                         }
                         JSON_CATCH (std::invalid_argument&)
                         {
-                            JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                            JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
                         break;
                     }
@@ -12502,7 +12543,7 @@ basic_json_parser_74:
                     */
                     default:
                     {
-                        JSON_THROW(type_error(313, "invalid value to unflatten"));
+                        JSON_THROW(type_error::create(313, "invalid value to unflatten"));
                     }
                 }
             }
@@ -12570,7 +12611,7 @@ basic_json_parser_74:
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
+                            JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
                         }
 
                         if (reference_token == "-")
@@ -12587,7 +12628,7 @@ basic_json_parser_74:
                             }
                             JSON_CATCH (std::invalid_argument&)
                             {
-                                JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                                JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                             }
                         }
                         break;
@@ -12595,7 +12636,7 @@ basic_json_parser_74:
 
                     default:
                     {
-                        JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'"));
+                        JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -12627,15 +12668,15 @@ basic_json_parser_74:
                         if (reference_token == "-")
                         {
                             // "-" always fails the range check
-                            JSON_THROW(out_of_range(402, "array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range"));
+                            JSON_THROW(out_of_range::create(402, "array index '-' (" +
+                                                            std::to_string(ptr->m_value.array->size()) +
+                                                            ") is out of range"));
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
+                            JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
                         }
 
                         // note: at performs range check
@@ -12645,14 +12686,14 @@ basic_json_parser_74:
                         }
                         JSON_CATCH (std::invalid_argument&)
                         {
-                            JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                            JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
                         break;
                     }
 
                     default:
                     {
-                        JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'"));
+                        JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -12691,15 +12732,15 @@ basic_json_parser_74:
                         if (reference_token == "-")
                         {
                             // "-" cannot be used for const access
-                            JSON_THROW(out_of_range(402, "array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range"));
+                            JSON_THROW(out_of_range::create(402, "array index '-' (" +
+                                                            std::to_string(ptr->m_value.array->size()) +
+                                                            ") is out of range"));
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
+                            JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
                         }
 
                         // use unchecked array access
@@ -12709,14 +12750,14 @@ basic_json_parser_74:
                         }
                         JSON_CATCH (std::invalid_argument&)
                         {
-                            JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                            JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
                         break;
                     }
 
                     default:
                     {
-                        JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'"));
+                        JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -12748,15 +12789,15 @@ basic_json_parser_74:
                         if (reference_token == "-")
                         {
                             // "-" always fails the range check
-                            JSON_THROW(out_of_range(402, "array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range"));
+                            JSON_THROW(out_of_range::create(402, "array index '-' (" +
+                                                            std::to_string(ptr->m_value.array->size()) +
+                                                            ") is out of range"));
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
+                            JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
                         }
 
                         // note: at performs range check
@@ -12766,14 +12807,14 @@ basic_json_parser_74:
                         }
                         JSON_CATCH (std::invalid_argument&)
                         {
-                            JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                            JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
                         break;
                     }
 
                     default:
                     {
-                        JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'"));
+                        JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -12803,7 +12844,7 @@ basic_json_parser_74:
             // check if nonempty reference string begins with slash
             if (reference_string[0] != '/')
             {
-                JSON_THROW(parse_error(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'"));
+                JSON_THROW(parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'"));
             }
 
             // extract the reference tokens:
@@ -12838,7 +12879,7 @@ basic_json_parser_74:
                             (reference_token[pos + 1] != '0' and
                              reference_token[pos + 1] != '1'))
                     {
-                        JSON_THROW(parse_error(108, 0, "escape character '~' must be followed with '0' or '1'"));
+                        JSON_THROW(parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
                     }
                 }
 
@@ -12969,7 +13010,7 @@ basic_json_parser_74:
         {
             if (not value.is_object())
             {
-                JSON_THROW(type_error(314, "only objects can be unflattened"));
+                JSON_THROW(type_error::create(314, "only objects can be unflattened"));
             }
 
             basic_json result;
@@ -12979,7 +13020,7 @@ basic_json_parser_74:
             {
                 if (not element.second.is_primitive())
                 {
-                    JSON_THROW(type_error(315, "values in object must be primitive"));
+                    JSON_THROW(type_error::create(315, "values in object must be primitive"));
                 }
 
                 // assign value to reference pointed to by JSON pointer; Note
@@ -13364,7 +13405,7 @@ basic_json_parser_74:
                             if (static_cast<size_type>(idx) > parent.size())
                             {
                                 // avoid undefined behavior
-                                JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range"));
+                                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
                             }
                             else
                             {
@@ -13402,7 +13443,7 @@ basic_json_parser_74:
                 }
                 else
                 {
-                    JSON_THROW(out_of_range(403, "key '" + last_path + "' not found"));
+                    JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found"));
                 }
             }
             else if (parent.is_array())
@@ -13415,7 +13456,7 @@ basic_json_parser_74:
         // type check: top level value must be an array
         if (not json_patch.is_array())
         {
-            JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects"));
+            JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
         }
 
         // iterate and apply the operations
@@ -13435,13 +13476,13 @@ basic_json_parser_74:
                 // check if desired value is present
                 if (it == val.m_value.object->end())
                 {
-                    JSON_THROW(parse_error(105, 0, error_msg + " must have member '" + member + "'"));
+                    JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'"));
                 }
 
                 // check if result is of type string
                 if (string_type and not it->second.is_string())
                 {
-                    JSON_THROW(parse_error(105, 0, error_msg + " must have string member '" + member + "'"));
+                    JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'"));
                 }
 
                 // no error: return value
@@ -13451,7 +13492,7 @@ basic_json_parser_74:
             // type check: every element of the array must be an object
             if (not val.is_object())
             {
-                JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects"));
+                JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
             }
 
             // collect mandatory members
@@ -13524,7 +13565,7 @@ basic_json_parser_74:
                     // throw an exception if test fails
                     if (not success)
                     {
-                        JSON_THROW(other_error(501, "unsuccessful: " + val.dump()));
+                        JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump()));
                     }
 
                     break;
@@ -13534,7 +13575,7 @@ basic_json_parser_74:
                 {
                     // op must be "add", "remove", "replace", "move", "copy", or
                     // "test"
-                    JSON_THROW(parse_error(105, 0, "operation value '" + op + "' is invalid"));
+                    JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid"));
                 }
             }
         }
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 1480a5f5..d5b12d2c 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -116,29 +116,34 @@ namespace detail
 
 Extension of std::exception objects with a member @a id for exception ids.
 
+@note To have nothrow-copy-constructible exceptions, we inherit from
+      std::runtime_error which can cope with arbitrary-length error messages.
+      Intermediate strings are built with static functions and then passed to
+      the actual constructor.
+
 @since version 3.0.0
 */
-class exception : public std::exception
+class exception : public std::runtime_error
 {
   public:
-    /// create exception with id an explanatory string
-    exception(int id_, const std::string& ename, const std::string& what_arg_)
-        : id(id_),
-          what_arg("[json.exception." + ename + "." + std::to_string(id_) + "] " + what_arg_)
-    {}
-
     /// returns the explanatory string
     virtual const char* what() const noexcept override
     {
-        return what_arg.c_str();
+        return std::runtime_error::what();
     }
 
     /// the id of the exception
     const int id;
 
-  private:
-    /// the explanatory string
-    const std::string what_arg;
+  protected:
+    exception(int id_, const char* what_arg)
+        : std::runtime_error(what_arg), id(id_)
+    {}
+
+    static std::string name(const std::string& ename, int id_)
+    {
+        return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
+    }
 };
 
 /*!
@@ -184,14 +189,16 @@ class parse_error : public exception
     @param[in] id_        the id of the exception
     @param[in] byte_      the byte index where the error occured (or 0 if
                           the position cannot be determined)
-    @param[in] what_arg_  the explanatory string
+    @param[in] what_arg   the explanatory string
+    @return parse_error object
     */
-    parse_error(int id_, size_t byte_, const std::string& what_arg_)
-        : exception(id_, "parse_error", "parse error" +
-                    (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
-                    ": " + what_arg_),
-          byte(byte_)
-    {}
+    static parse_error create(int id_, size_t byte_, const std::string& what_arg)
+    {
+        std::string w = exception::name("parse_error", id_) + "parse error" +
+                        (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
+                        ": " + what_arg;
+        return parse_error(id_, byte_, w.c_str());
+    }
 
     /*!
     @brief byte index of the parse error
@@ -204,6 +211,12 @@ class parse_error : public exception
           MessagePack).
     */
     const size_t byte;
+
+  private:
+    parse_error(int id_, size_t byte_, const char* what_arg)
+        : exception(id_, what_arg),
+          byte(byte_)
+    {}
 };
 
 /*!
@@ -233,8 +246,15 @@ json.exception.invalid_iterator.214 | cannot get value | Cannot get value for it
 class invalid_iterator : public exception
 {
   public:
-    invalid_iterator(int id_, const std::string& what_arg_)
-        : exception(id_, "invalid_iterator", what_arg_)
+    static invalid_iterator create(int id_, const std::string& what_arg)
+    {
+        std::string w = exception::name("invalid_iterator", id_) + what_arg;
+        return invalid_iterator(id_, w.c_str());
+    }
+
+  private:
+    invalid_iterator(int id_, const char* what_arg)
+        : exception(id_, what_arg)
     {}
 };
 
@@ -265,8 +285,15 @@ json.exception.type_error.315 | values in object must be primitive | The @ref un
 class type_error : public exception
 {
   public:
-    type_error(int id_, const std::string& what_arg_)
-        : exception(id_, "type_error", what_arg_)
+    static type_error create(int id_, const std::string& what_arg)
+    {
+        std::string w = exception::name("type_error", id_) + what_arg;
+        return type_error(id_, w.c_str());
+    }
+
+  private:
+    type_error(int id_, const char* what_arg)
+        : exception(id_, what_arg)
     {}
 };
 
@@ -289,8 +316,15 @@ json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed n
 class out_of_range : public exception
 {
   public:
-    out_of_range(int id_, const std::string& what_arg_)
-        : exception(id_, "out_of_range", what_arg_)
+    static out_of_range create(int id_, const std::string& what_arg)
+    {
+        std::string w = exception::name("out_of_range", id_) + what_arg;
+        return out_of_range(id_, w.c_str());
+    }
+
+  private:
+    out_of_range(int id_, const char* what_arg)
+        : exception(id_, what_arg)
     {}
 };
 
@@ -308,8 +342,15 @@ json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "valu
 class other_error : public exception
 {
   public:
-    other_error(int id_, const std::string& what_arg_)
-        : exception(id_, "other_error", what_arg_)
+    static other_error create(int id_, const std::string& what_arg)
+    {
+        std::string w = exception::name("other_error", id_) + what_arg;
+        return other_error(id_, w.c_str());
+    }
+
+  private:
+    other_error(int id_, const char* what_arg)
+        : exception(id_, what_arg)
     {}
 };
 
@@ -841,7 +882,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
         }
         default:
         {
-            JSON_THROW(type_error(302, "type must be number, but is " + j.type_name()));
+            JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name()));
         }
     }
 }
@@ -851,7 +892,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
 {
     if (not j.is_boolean())
     {
-        JSON_THROW(type_error(302, "type must be boolean, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be boolean, but is " + j.type_name()));
     }
     b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
 }
@@ -861,7 +902,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
 {
     if (not j.is_string())
     {
-        JSON_THROW(type_error(302, "type must be string, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be string, but is " + j.type_name()));
     }
     s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
 }
@@ -898,7 +939,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
 {
     if (not j.is_array())
     {
-        JSON_THROW(type_error(302, "type must be array, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name()));
     }
     arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
 }
@@ -910,7 +951,7 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
 {
     if (not j.is_array())
     {
-        JSON_THROW(type_error(302, "type must be array, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name()));
     }
 
     for (auto it = j.rbegin(), end = j.rend(); it != end; ++it)
@@ -961,7 +1002,7 @@ void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
 {
     if (not j.is_array())
     {
-        JSON_THROW(type_error(302, "type must be array, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be array, but is " + j.type_name()));
     }
 
     from_json_array_impl(j, arr, priority_tag<1> {});
@@ -973,7 +1014,7 @@ void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
 {
     if (not j.is_object())
     {
-        JSON_THROW(type_error(302, "type must be object, but is " + j.type_name()));
+        JSON_THROW(type_error::create(302, "type must be object, but is " + j.type_name()));
     }
 
     auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
@@ -1023,7 +1064,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val)
         }
         default:
         {
-            JSON_THROW(type_error(302, "type must be number, but is " + j.type_name()));
+            JSON_THROW(type_error::create(302, "type must be number, but is " + j.type_name()));
         }
     }
 }
@@ -1970,7 +2011,7 @@ class basic_json
                 {
                     if (t == value_t::null)
                     {
-                        JSON_THROW(other_error(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE
+                        JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE
                     }
                     break;
                 }
@@ -2325,7 +2366,7 @@ class basic_json
             // if object is wanted but impossible, throw an exception
             if (manual_type == value_t::object and not is_an_object)
             {
-                JSON_THROW(type_error(301, "cannot create object from initializer list"));
+                JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
             }
         }
 
@@ -2509,7 +2550,7 @@ class basic_json
         // make sure iterator fits the current value
         if (first.m_object != last.m_object)
         {
-            JSON_THROW(invalid_iterator(201, "iterators are not compatible"));
+            JSON_THROW(invalid_iterator::create(201, "iterators are not compatible"));
         }
 
         // copy type from first iterator
@@ -2526,7 +2567,7 @@ class basic_json
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    JSON_THROW(invalid_iterator(204, "iterators out of range"));
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
                 }
                 break;
             }
@@ -2585,8 +2626,8 @@ class basic_json
 
             default:
             {
-                JSON_THROW(invalid_iterator(206, "cannot construct with iterators from " +
-                                            first.m_object->type_name()));
+                JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " +
+                                                    first.m_object->type_name()));
             }
         }
 
@@ -3224,7 +3265,7 @@ class basic_json
             return m_value.boolean;
         }
 
-        JSON_THROW(type_error(302, "type must be boolean, but is " + type_name()));
+        JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name()));
     }
 
     /// get a pointer to the value (object)
@@ -3336,7 +3377,7 @@ class basic_json
             return *ptr;
         }
 
-        JSON_THROW(type_error(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name()));
+        JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name()));
     }
 
   public:
@@ -3738,12 +3779,12 @@ class basic_json
             JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range"));
+                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
             }
         }
         else
         {
-            JSON_THROW(type_error(304, "cannot use at() with " + type_name()));
+            JSON_THROW(type_error::create(304, "cannot use at() with " + type_name()));
         }
     }
 
@@ -3785,12 +3826,12 @@ class basic_json
             JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range"));
+                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
             }
         }
         else
         {
-            JSON_THROW(type_error(304, "cannot use at() with " + type_name()));
+            JSON_THROW(type_error::create(304, "cannot use at() with " + type_name()));
         }
     }
 
@@ -3836,12 +3877,12 @@ class basic_json
             JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                JSON_THROW(out_of_range(403, "key '" + key + "' not found"));
+                JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
             }
         }
         else
         {
-            JSON_THROW(type_error(304, "cannot use at() with " + type_name()));
+            JSON_THROW(type_error::create(304, "cannot use at() with " + type_name()));
         }
     }
 
@@ -3887,12 +3928,12 @@ class basic_json
             JSON_CATCH (std::out_of_range&)
             {
                 // create better exception explanation
-                JSON_THROW(out_of_range(403, "key '" + key + "' not found"));
+                JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
             }
         }
         else
         {
-            JSON_THROW(type_error(304, "cannot use at() with " + type_name()));
+            JSON_THROW(type_error::create(304, "cannot use at() with " + type_name()));
         }
     }
 
@@ -3945,7 +3986,7 @@ class basic_json
             return m_value.array->operator[](idx);
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -3975,7 +4016,7 @@ class basic_json
             return m_value.array->operator[](idx);
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4021,7 +4062,7 @@ class basic_json
             return m_value.object->operator[](key);
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4063,7 +4104,7 @@ class basic_json
             return m_value.object->find(key)->second;
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4178,7 +4219,7 @@ class basic_json
             return m_value.object->operator[](key);
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4221,7 +4262,7 @@ class basic_json
             return m_value.object->find(key)->second;
         }
 
-        JSON_THROW(type_error(305, "cannot use operator[] with " + type_name()));
+        JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name()));
     }
 
     /*!
@@ -4290,7 +4331,7 @@ class basic_json
         }
         else
         {
-            JSON_THROW(type_error(306, "cannot use value() with " + type_name()));
+            JSON_THROW(type_error::create(306, "cannot use value() with " + type_name()));
         }
     }
 
@@ -4362,7 +4403,7 @@ class basic_json
             }
         }
 
-        JSON_THROW(type_error(306, "cannot use value() with " + type_name()));
+        JSON_THROW(type_error::create(306, "cannot use value() with " + type_name()));
     }
 
     /*!
@@ -4515,7 +4556,7 @@ class basic_json
         // make sure iterator fits the current value
         if (this != pos.m_object)
         {
-            JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
         }
 
         IteratorType result = end();
@@ -4530,7 +4571,7 @@ class basic_json
             {
                 if (not pos.m_it.primitive_iterator.is_begin())
                 {
-                    JSON_THROW(invalid_iterator(205, "iterator out of range"));
+                    JSON_THROW(invalid_iterator::create(205, "iterator out of range"));
                 }
 
                 if (is_string())
@@ -4560,7 +4601,7 @@ class basic_json
 
             default:
             {
-                JSON_THROW(type_error(307, "cannot use erase() with " + type_name()));
+                JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name()));
             }
         }
 
@@ -4622,7 +4663,7 @@ class basic_json
         // make sure iterator fits the current value
         if (this != first.m_object or this != last.m_object)
         {
-            JSON_THROW(invalid_iterator(203, "iterators do not fit current value"));
+            JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value"));
         }
 
         IteratorType result = end();
@@ -4637,7 +4678,7 @@ class basic_json
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    JSON_THROW(invalid_iterator(204, "iterators out of range"));
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
                 }
 
                 if (is_string())
@@ -4669,7 +4710,7 @@ class basic_json
 
             default:
             {
-                JSON_THROW(type_error(307, "cannot use erase() with " + type_name()));
+                JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name()));
             }
         }
 
@@ -4713,7 +4754,7 @@ class basic_json
             return m_value.object->erase(key);
         }
 
-        JSON_THROW(type_error(307, "cannot use erase() with " + type_name()));
+        JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name()));
     }
 
     /*!
@@ -4747,14 +4788,14 @@ class basic_json
         {
             if (idx >= size())
             {
-                JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range"));
+                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
             }
 
             m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
         }
         else
         {
-            JSON_THROW(type_error(307, "cannot use erase() with " + type_name()));
+            JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name()));
         }
     }
 
@@ -5472,7 +5513,7 @@ class basic_json
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            JSON_THROW(type_error(308, "cannot use push_back() with " + type_name()));
+            JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -5508,7 +5549,7 @@ class basic_json
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            JSON_THROW(type_error(308, "cannot use push_back() with " + type_name()));
+            JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -5558,7 +5599,7 @@ class basic_json
         // push_back only works for null objects or objects
         if (not(is_null() or is_object()))
         {
-            JSON_THROW(type_error(308, "cannot use push_back() with " + type_name()));
+            JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name()));
         }
 
         // transform null object into an object
@@ -5658,7 +5699,7 @@ class basic_json
         // emplace_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            JSON_THROW(type_error(311, "cannot use emplace_back() with " + type_name()));
+            JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name()));
         }
 
         // transform null object into an array
@@ -5706,7 +5747,7 @@ class basic_json
         // emplace only works for null objects or arrays
         if (not(is_null() or is_object()))
         {
-            JSON_THROW(type_error(311, "cannot use emplace() with " + type_name()));
+            JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name()));
         }
 
         // transform null object into an object
@@ -5757,7 +5798,7 @@ class basic_json
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
             }
 
             // insert to array and return iterator
@@ -5766,7 +5807,7 @@ class basic_json
             return result;
         }
 
-        JSON_THROW(type_error(309, "cannot use insert() with " + type_name()));
+        JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name()));
     }
 
     /*!
@@ -5810,7 +5851,7 @@ class basic_json
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
             }
 
             // insert to array and return iterator
@@ -5819,7 +5860,7 @@ class basic_json
             return result;
         }
 
-        JSON_THROW(type_error(309, "cannot use insert() with " + type_name()));
+        JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name()));
     }
 
     /*!
@@ -5857,24 +5898,24 @@ class basic_json
         // insert only works for arrays
         if (not is_array())
         {
-            JSON_THROW(type_error(309, "cannot use insert() with " + type_name()));
+            JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name()));
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
         }
 
         // check if range iterators belong to the same JSON object
         if (first.m_object != last.m_object)
         {
-            JSON_THROW(invalid_iterator(210, "iterators do not fit"));
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
         }
 
         if (first.m_object == this or last.m_object == this)
         {
-            JSON_THROW(invalid_iterator(211, "passed iterators may not belong to container"));
+            JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
         }
 
         // insert to array and return iterator
@@ -5915,13 +5956,13 @@ class basic_json
         // insert only works for arrays
         if (not is_array())
         {
-            JSON_THROW(type_error(309, "cannot use insert() with " + type_name()));
+            JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name()));
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            JSON_THROW(invalid_iterator(202, "iterator does not fit current value"));
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
         }
 
         // insert to array and return iterator
@@ -5988,7 +6029,7 @@ class basic_json
         }
         else
         {
-            JSON_THROW(type_error(310, "cannot use swap() with " + type_name()));
+            JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name()));
         }
     }
 
@@ -6021,7 +6062,7 @@ class basic_json
         }
         else
         {
-            JSON_THROW(type_error(310, "cannot use swap() with " + type_name()));
+            JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name()));
         }
     }
 
@@ -6054,7 +6095,7 @@ class basic_json
         }
         else
         {
-            JSON_THROW(type_error(310, "cannot use swap() with " + type_name()));
+            JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name()));
         }
     }
 
@@ -7999,19 +8040,19 @@ class basic_json
         // simple case: requested length is greater than the vector's length
         if (len > size or offset > size)
         {
-            JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
+            JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
         }
 
         // second case: adding offset would result in overflow
         if ((size > ((std::numeric_limits<size_t>::max)() - offset)))
         {
-            JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
+            JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
         }
 
         // last case: reading past the end of the vector
         if (len + offset > size)
         {
-            JSON_THROW(parse_error(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
+            JSON_THROW(parse_error::create(110, offset + 1, "cannot read " + std::to_string(len) + " bytes from vector"));
         }
     }
 
@@ -8043,7 +8084,7 @@ class basic_json
 
         std::stringstream ss;
         ss << std::hex << static_cast<int>(v[idx]);
-        JSON_THROW(parse_error(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str()));
+        JSON_THROW(parse_error::create(113, idx + 1, "expected a MessagePack string; last byte: 0x" + ss.str()));
     }
 
     /*!
@@ -8073,7 +8114,7 @@ class basic_json
 
         std::stringstream ss;
         ss << std::hex << static_cast<int>(v[idx]);
-        JSON_THROW(parse_error(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str()));
+        JSON_THROW(parse_error::create(113, idx + 1, "expected a CBOR string; last byte: 0x" + ss.str()));
     }
 
     /*!
@@ -8316,7 +8357,7 @@ class basic_json
                 {
                     std::stringstream ss;
                     ss << std::hex << static_cast<int>(v[current_idx]);
-                    JSON_THROW(parse_error(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str()));
+                    JSON_THROW(parse_error::create(112, current_idx + 1, "error reading MessagePack; last byte: 0x" + ss.str()));
                 }
             }
         }
@@ -8816,7 +8857,7 @@ class basic_json
             {
                 std::stringstream ss;
                 ss << std::hex << static_cast<int>(v[current_idx]);
-                JSON_THROW(parse_error(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str()));
+                JSON_THROW(parse_error::create(112, current_idx + 1, "error reading CBOR; last byte: 0x" + ss.str()));
             }
         }
     }
@@ -9697,7 +9738,7 @@ class basic_json
 
                 case basic_json::value_t::null:
                 {
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
 
                 default:
@@ -9707,7 +9748,7 @@ class basic_json
                         return *m_object;
                     }
 
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
             }
         }
@@ -9741,7 +9782,7 @@ class basic_json
                         return m_object;
                     }
 
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
             }
         }
@@ -9841,7 +9882,7 @@ class basic_json
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers"));
+                JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
             }
 
             assert(m_object != nullptr);
@@ -9883,7 +9924,7 @@ class basic_json
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                JSON_THROW(invalid_iterator(212, "cannot compare iterators of different containers"));
+                JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
             }
 
             assert(m_object != nullptr);
@@ -9892,7 +9933,7 @@ class basic_json
             {
                 case basic_json::value_t::object:
                 {
-                    JSON_THROW(invalid_iterator(213, "cannot compare order of object iterators"));
+                    JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -9946,7 +9987,7 @@ class basic_json
             {
                 case basic_json::value_t::object:
                 {
-                    JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators"));
+                    JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -10008,7 +10049,7 @@ class basic_json
             {
                 case basic_json::value_t::object:
                 {
-                    JSON_THROW(invalid_iterator(209, "cannot use offsets with object iterators"));
+                    JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -10035,7 +10076,7 @@ class basic_json
             {
                 case basic_json::value_t::object:
                 {
-                    JSON_THROW(invalid_iterator(208, "cannot use operator[] for object iterators"));
+                    JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
                 }
 
                 case basic_json::value_t::array:
@@ -10045,7 +10086,7 @@ class basic_json
 
                 case basic_json::value_t::null:
                 {
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
 
                 default:
@@ -10055,7 +10096,7 @@ class basic_json
                         return *m_object;
                     }
 
-                    JSON_THROW(invalid_iterator(214, "cannot get value"));
+                    JSON_THROW(invalid_iterator::create(214, "cannot get value"));
                 }
             }
         }
@@ -10073,7 +10114,7 @@ class basic_json
                 return m_it.object_iterator->first;
             }
 
-            JSON_THROW(invalid_iterator(207, "cannot use key() for non-object iterators"));
+            JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators"));
         }
 
         /*!
@@ -10263,7 +10304,7 @@ class basic_json
             // immediately abort if stream is erroneous
             if (s.fail())
             {
-                JSON_THROW(parse_error(111, 0, "bad input stream"));
+                JSON_THROW(parse_error::create(111, 0, "bad input stream"));
             }
 
             // fill buffer
@@ -10330,7 +10371,7 @@ class basic_json
                 }
                 else
                 {
-                    JSON_THROW(parse_error(102, get_position(), "missing or wrong low surrogate"));
+                    JSON_THROW(parse_error::create(102, get_position(), "missing or wrong low surrogate"));
                 }
             }
 
@@ -10364,7 +10405,7 @@ class basic_json
             }
             else
             {
-                JSON_THROW(parse_error(103, get_position(), "code points above 0x10FFFF are invalid"));
+                JSON_THROW(parse_error::create(103, get_position(), "code points above 0x10FFFF are invalid"));
             }
 
             return result;
@@ -10593,7 +10634,7 @@ class basic_json
                 // check if stream is still good
                 if (m_stream->fail())
                 {
-                    JSON_THROW(parse_error(111, 0, "bad input stream"));
+                    JSON_THROW(parse_error::create(111, 0, "bad input stream"));
                 }
 
                 std::getline(*m_stream, m_line_buffer_tmp, '\n');
@@ -10762,7 +10803,7 @@ class basic_json
                                 // make sure there is a subsequent unicode
                                 if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u')
                                 {
-                                    JSON_THROW(parse_error(102, get_position(), "missing low surrogate"));
+                                    JSON_THROW(parse_error::create(102, get_position(), "missing low surrogate"));
                                 }
 
                                 // get code yyyy from uxxxx\uyyyy
@@ -10775,7 +10816,7 @@ class basic_json
                             else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF)
                             {
                                 // we found a lone low surrogate
-                                JSON_THROW(parse_error(102, get_position(), "missing high surrogate"));
+                                JSON_THROW(parse_error::create(102, get_position(), "missing high surrogate"));
                             }
                             else
                             {
@@ -11019,7 +11060,7 @@ class basic_json
                 // throw in case of infinity or NAN
                 if (not std::isfinite(result.m_value.number_float))
                 {
-                    JSON_THROW(out_of_range(406, "number overflow parsing '" + get_token_string() + "'"));
+                    JSON_THROW(out_of_range::create(406, "number overflow parsing '" + get_token_string() + "'"));
                 }
 
                 return true;
@@ -11332,7 +11373,7 @@ class basic_json
                               "'") :
                               lexer::token_type_name(last_token));
                 error_msg += "; expected " + lexer::token_type_name(t);
-                JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg));
+                JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
             }
         }
 
@@ -11347,7 +11388,7 @@ class basic_json
                 error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token_string() +
                               "'") :
                               lexer::token_type_name(last_token));
-                JSON_THROW(parse_error(101, m_lexer.get_position(), error_msg));
+                JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
             }
         }
 
@@ -11446,7 +11487,7 @@ class basic_json
         {
             if (is_root())
             {
-                JSON_THROW(out_of_range(405, "JSON pointer has no parent"));
+                JSON_THROW(out_of_range::create(405, "JSON pointer has no parent"));
             }
 
             auto last = reference_tokens.back();
@@ -11464,7 +11505,7 @@ class basic_json
         {
             if (is_root())
             {
-                JSON_THROW(out_of_range(405, "JSON pointer has no parent"));
+                JSON_THROW(out_of_range::create(405, "JSON pointer has no parent"));
             }
 
             json_pointer result = *this;
@@ -11521,7 +11562,7 @@ class basic_json
                         }
                         JSON_CATCH (std::invalid_argument&)
                         {
-                            JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                            JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
                         break;
                     }
@@ -11535,7 +11576,7 @@ class basic_json
                     */
                     default:
                     {
-                        JSON_THROW(type_error(313, "invalid value to unflatten"));
+                        JSON_THROW(type_error::create(313, "invalid value to unflatten"));
                     }
                 }
             }
@@ -11603,7 +11644,7 @@ class basic_json
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
+                            JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
                         }
 
                         if (reference_token == "-")
@@ -11620,7 +11661,7 @@ class basic_json
                             }
                             JSON_CATCH (std::invalid_argument&)
                             {
-                                JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                                JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                             }
                         }
                         break;
@@ -11628,7 +11669,7 @@ class basic_json
 
                     default:
                     {
-                        JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'"));
+                        JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -11660,15 +11701,15 @@ class basic_json
                         if (reference_token == "-")
                         {
                             // "-" always fails the range check
-                            JSON_THROW(out_of_range(402, "array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range"));
+                            JSON_THROW(out_of_range::create(402, "array index '-' (" +
+                                                            std::to_string(ptr->m_value.array->size()) +
+                                                            ") is out of range"));
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
+                            JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
                         }
 
                         // note: at performs range check
@@ -11678,14 +11719,14 @@ class basic_json
                         }
                         JSON_CATCH (std::invalid_argument&)
                         {
-                            JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                            JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
                         break;
                     }
 
                     default:
                     {
-                        JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'"));
+                        JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -11724,15 +11765,15 @@ class basic_json
                         if (reference_token == "-")
                         {
                             // "-" cannot be used for const access
-                            JSON_THROW(out_of_range(402, "array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range"));
+                            JSON_THROW(out_of_range::create(402, "array index '-' (" +
+                                                            std::to_string(ptr->m_value.array->size()) +
+                                                            ") is out of range"));
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
+                            JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
                         }
 
                         // use unchecked array access
@@ -11742,14 +11783,14 @@ class basic_json
                         }
                         JSON_CATCH (std::invalid_argument&)
                         {
-                            JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                            JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
                         break;
                     }
 
                     default:
                     {
-                        JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'"));
+                        JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -11781,15 +11822,15 @@ class basic_json
                         if (reference_token == "-")
                         {
                             // "-" always fails the range check
-                            JSON_THROW(out_of_range(402, "array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range"));
+                            JSON_THROW(out_of_range::create(402, "array index '-' (" +
+                                                            std::to_string(ptr->m_value.array->size()) +
+                                                            ") is out of range"));
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            JSON_THROW(parse_error(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
+                            JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
                         }
 
                         // note: at performs range check
@@ -11799,14 +11840,14 @@ class basic_json
                         }
                         JSON_CATCH (std::invalid_argument&)
                         {
-                            JSON_THROW(parse_error(109, 0, "array index '" + reference_token + "' is not a number"));
+                            JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
                         }
                         break;
                     }
 
                     default:
                     {
-                        JSON_THROW(out_of_range(404, "unresolved reference token '" + reference_token + "'"));
+                        JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
                     }
                 }
             }
@@ -11836,7 +11877,7 @@ class basic_json
             // check if nonempty reference string begins with slash
             if (reference_string[0] != '/')
             {
-                JSON_THROW(parse_error(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'"));
+                JSON_THROW(parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'"));
             }
 
             // extract the reference tokens:
@@ -11871,7 +11912,7 @@ class basic_json
                             (reference_token[pos + 1] != '0' and
                              reference_token[pos + 1] != '1'))
                     {
-                        JSON_THROW(parse_error(108, 0, "escape character '~' must be followed with '0' or '1'"));
+                        JSON_THROW(parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
                     }
                 }
 
@@ -12002,7 +12043,7 @@ class basic_json
         {
             if (not value.is_object())
             {
-                JSON_THROW(type_error(314, "only objects can be unflattened"));
+                JSON_THROW(type_error::create(314, "only objects can be unflattened"));
             }
 
             basic_json result;
@@ -12012,7 +12053,7 @@ class basic_json
             {
                 if (not element.second.is_primitive())
                 {
-                    JSON_THROW(type_error(315, "values in object must be primitive"));
+                    JSON_THROW(type_error::create(315, "values in object must be primitive"));
                 }
 
                 // assign value to reference pointed to by JSON pointer; Note
@@ -12397,7 +12438,7 @@ class basic_json
                             if (static_cast<size_type>(idx) > parent.size())
                             {
                                 // avoid undefined behavior
-                                JSON_THROW(out_of_range(401, "array index " + std::to_string(idx) + " is out of range"));
+                                JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
                             }
                             else
                             {
@@ -12435,7 +12476,7 @@ class basic_json
                 }
                 else
                 {
-                    JSON_THROW(out_of_range(403, "key '" + last_path + "' not found"));
+                    JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found"));
                 }
             }
             else if (parent.is_array())
@@ -12448,7 +12489,7 @@ class basic_json
         // type check: top level value must be an array
         if (not json_patch.is_array())
         {
-            JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects"));
+            JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
         }
 
         // iterate and apply the operations
@@ -12468,13 +12509,13 @@ class basic_json
                 // check if desired value is present
                 if (it == val.m_value.object->end())
                 {
-                    JSON_THROW(parse_error(105, 0, error_msg + " must have member '" + member + "'"));
+                    JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'"));
                 }
 
                 // check if result is of type string
                 if (string_type and not it->second.is_string())
                 {
-                    JSON_THROW(parse_error(105, 0, error_msg + " must have string member '" + member + "'"));
+                    JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'"));
                 }
 
                 // no error: return value
@@ -12484,7 +12525,7 @@ class basic_json
             // type check: every element of the array must be an object
             if (not val.is_object())
             {
-                JSON_THROW(parse_error(104, 0, "JSON patch must be an array of objects"));
+                JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
             }
 
             // collect mandatory members
@@ -12557,7 +12598,7 @@ class basic_json
                     // throw an exception if test fails
                     if (not success)
                     {
-                        JSON_THROW(other_error(501, "unsuccessful: " + val.dump()));
+                        JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump()));
                     }
 
                     break;
@@ -12567,7 +12608,7 @@ class basic_json
                 {
                     // op must be "add", "remove", "replace", "move", "copy", or
                     // "test"
-                    JSON_THROW(parse_error(105, 0, "operation value '" + op + "' is invalid"));
+                    JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid"));
                 }
             }
         }
diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp
index 898e7796..ed362f65 100644
--- a/test/src/unit-noexcept.cpp
+++ b/test/src/unit-noexcept.cpp
@@ -57,3 +57,11 @@ static_assert(noexcept(json(pod{})), "");
 static_assert(noexcept(j.get<pod>()), "");
 static_assert(not noexcept(j.get<pod_bis>()), "");
 static_assert(noexcept(json(pod{})), "");
+
+// for ERR60-CPP (https://github.com/nlohmann/json/issues/531)
+static_assert(std::is_nothrow_copy_constructible<json::exception>::value, "json::exception must be nothrow copy constructible");
+static_assert(std::is_nothrow_copy_constructible<json::parse_error>::value, "json::parse_error must be nothrow copy constructible");
+static_assert(std::is_nothrow_copy_constructible<json::invalid_iterator>::value, "json::invalid_iterator must be nothrow copy constructible");
+static_assert(std::is_nothrow_copy_constructible<json::type_error>::value, "json::type_error must be nothrow copy constructible");
+static_assert(std::is_nothrow_copy_constructible<json::out_of_range>::value, "json::out_of_range must be nothrow copy constructible");
+static_assert(std::is_nothrow_copy_constructible<json::other_error>::value, "json::other_error must be nothrow copy constructible");

From c333679a9653119ddf19613d3fdc2cb1d42e2a26 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sat, 25 Mar 2017 17:25:39 +0100
Subject: [PATCH 4/7] :hammer: small refactoring

The solution with a std::runtime_error member is more elegant. It
allows to have std::exception as base class again. However, I still
have no idea why GCC thinks the copy constructor may throw...
---
 src/json.hpp      | 9 ++++++---
 src/json.hpp.re2c | 9 ++++++---
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index bdada276..5ee3edad 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -123,13 +123,13 @@ Extension of std::exception objects with a member @a id for exception ids.
 
 @since version 3.0.0
 */
-class exception : public std::runtime_error
+class exception : public std::exception
 {
   public:
     /// returns the explanatory string
     virtual const char* what() const noexcept override
     {
-        return std::runtime_error::what();
+        return m.what();
     }
 
     /// the id of the exception
@@ -137,13 +137,16 @@ class exception : public std::runtime_error
 
   protected:
     exception(int id_, const char* what_arg)
-        : std::runtime_error(what_arg), id(id_)
+        : id(id_), m(what_arg)
     {}
 
     static std::string name(const std::string& ename, int id_)
     {
         return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
     }
+
+  private:
+    std::runtime_error m;
 };
 
 /*!
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index d5b12d2c..a51f77e7 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -123,13 +123,13 @@ Extension of std::exception objects with a member @a id for exception ids.
 
 @since version 3.0.0
 */
-class exception : public std::runtime_error
+class exception : public std::exception
 {
   public:
     /// returns the explanatory string
     virtual const char* what() const noexcept override
     {
-        return std::runtime_error::what();
+        return m.what();
     }
 
     /// the id of the exception
@@ -137,13 +137,16 @@ class exception : public std::runtime_error
 
   protected:
     exception(int id_, const char* what_arg)
-        : std::runtime_error(what_arg), id(id_)
+        : id(id_), m(what_arg)
     {}
 
     static std::string name(const std::string& ename, int id_)
     {
         return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
     }
+
+  private:
+    std::runtime_error m;
 };
 
 /*!

From cf7786887c7ca80d86112f6b9f0aeba8cb1f2f9c Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sat, 25 Mar 2017 17:35:57 +0100
Subject: [PATCH 5/7] :hammer: fixed check for is_nothrow_copy_constructible

We now only demand our exceptions to be is_nothrow_copy_constructible
if std::runtime_exception is.
---
 test/src/unit-noexcept.cpp | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp
index ed362f65..f2fbc8e6 100644
--- a/test/src/unit-noexcept.cpp
+++ b/test/src/unit-noexcept.cpp
@@ -58,10 +58,19 @@ static_assert(noexcept(j.get<pod>()), "");
 static_assert(not noexcept(j.get<pod_bis>()), "");
 static_assert(noexcept(json(pod{})), "");
 
-// for ERR60-CPP (https://github.com/nlohmann/json/issues/531)
-static_assert(std::is_nothrow_copy_constructible<json::exception>::value, "json::exception must be nothrow copy constructible");
-static_assert(std::is_nothrow_copy_constructible<json::parse_error>::value, "json::parse_error must be nothrow copy constructible");
-static_assert(std::is_nothrow_copy_constructible<json::invalid_iterator>::value, "json::invalid_iterator must be nothrow copy constructible");
-static_assert(std::is_nothrow_copy_constructible<json::type_error>::value, "json::type_error must be nothrow copy constructible");
-static_assert(std::is_nothrow_copy_constructible<json::out_of_range>::value, "json::out_of_range must be nothrow copy constructible");
-static_assert(std::is_nothrow_copy_constructible<json::other_error>::value, "json::other_error must be nothrow copy constructible");
+TEST_CASE("runtime checks")
+{
+    SECTION("nothrow-copy-constructible exceptions")
+    {
+        // for ERR60-CPP (https://github.com/nlohmann/json/issues/531)
+        if (std::is_nothrow_copy_constructible<std::runtime_error>::value)
+        {
+            CHECK(std::is_nothrow_copy_constructible<json::exception>::value);
+            CHECK(std::is_nothrow_copy_constructible<json::parse_error>::value);
+            CHECK(std::is_nothrow_copy_constructible<json::invalid_iterator>::value);
+            CHECK(std::is_nothrow_copy_constructible<json::type_error>::value);
+            CHECK(std::is_nothrow_copy_constructible<json::out_of_range>::value);
+            CHECK(std::is_nothrow_copy_constructible<json::other_error>::value);
+        }
+    }
+}

From 53b501a785f94250fe7b359b8ba6288d495aad5d Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sat, 25 Mar 2017 23:31:03 +0100
Subject: [PATCH 6/7] :hammer: cleanup

---
 src/json.hpp               | 64 +++++++++++++++++++-------------------
 src/json.hpp.re2c          | 62 ++++++++++++++++++------------------
 test/src/unit-noexcept.cpp | 21 +++++++------
 3 files changed, 74 insertions(+), 73 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 5ee3edad..522c601d 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -116,7 +116,7 @@ namespace detail
 
 Extension of std::exception objects with a member @a id for exception ids.
 
-@note To have nothrow-copy-constructible exceptions, we inherit from
+@note To have nothrow-copy-constructible exceptions, we internally use
       std::runtime_error which can cope with arbitrary-length error messages.
       Intermediate strings are built with static functions and then passed to
       the actual constructor.
@@ -136,16 +136,17 @@ class exception : public std::exception
     const int id;
 
   protected:
-    exception(int id_, const char* what_arg)
+    exception(int id_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
         : id(id_), m(what_arg)
     {}
 
-    static std::string name(const std::string& ename, int id_)
+    static std::string name(const std::string& ename, int id)
     {
-        return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
+        return "[json.exception." + ename + "." + std::to_string(id) + "] ";
     }
 
   private:
+    /// an exception object as storage for error messages
     std::runtime_error m;
 };
 
@@ -184,23 +185,23 @@ json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last
 
 @since version 3.0.0
 */
-class parse_error : public exception
+class parse_error : private exception
 {
   public:
     /*!
     @brief create a parse error exception
-    @param[in] id_        the id of the exception
+    @param[in] id         the id of the exception
     @param[in] byte_      the byte index where the error occured (or 0 if
                           the position cannot be determined)
     @param[in] what_arg   the explanatory string
     @return parse_error object
     */
-    static parse_error create(int id_, size_t byte_, const std::string& what_arg)
+    static parse_error create(int id, size_t byte_, const std::string& what_arg)
     {
-        std::string w = exception::name("parse_error", id_) + "parse error" +
+        std::string w = exception::name("parse_error", id) + "parse error" +
                         (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
                         ": " + what_arg;
-        return parse_error(id_, byte_, w.c_str());
+        return parse_error(id, byte_, w.c_str());
     }
 
     /*!
@@ -216,9 +217,8 @@ class parse_error : public exception
     const size_t byte;
 
   private:
-    parse_error(int id_, size_t byte_, const char* what_arg)
-        : exception(id_, what_arg),
-          byte(byte_)
+    parse_error(int id, size_t byte_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg), byte(byte_)
     {}
 };
 
@@ -249,15 +249,15 @@ json.exception.invalid_iterator.214 | cannot get value | Cannot get value for it
 class invalid_iterator : public exception
 {
   public:
-    static invalid_iterator create(int id_, const std::string& what_arg)
+    static invalid_iterator create(int id, const std::string& what_arg)
     {
-        std::string w = exception::name("invalid_iterator", id_) + what_arg;
-        return invalid_iterator(id_, w.c_str());
+        std::string w = exception::name("invalid_iterator", id) + what_arg;
+        return invalid_iterator(id, w.c_str());
     }
 
   private:
-    invalid_iterator(int id_, const char* what_arg)
-        : exception(id_, what_arg)
+    invalid_iterator(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg)
     {}
 };
 
@@ -288,15 +288,15 @@ json.exception.type_error.315 | values in object must be primitive | The @ref un
 class type_error : public exception
 {
   public:
-    static type_error create(int id_, const std::string& what_arg)
+    static type_error create(int id, const std::string& what_arg)
     {
-        std::string w = exception::name("type_error", id_) + what_arg;
-        return type_error(id_, w.c_str());
+        std::string w = exception::name("type_error", id) + what_arg;
+        return type_error(id, w.c_str());
     }
 
   private:
-    type_error(int id_, const char* what_arg)
-        : exception(id_, what_arg)
+    type_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg)
     {}
 };
 
@@ -319,15 +319,15 @@ json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed n
 class out_of_range : public exception
 {
   public:
-    static out_of_range create(int id_, const std::string& what_arg)
+    static out_of_range create(int id, const std::string& what_arg)
     {
-        std::string w = exception::name("out_of_range", id_) + what_arg;
-        return out_of_range(id_, w.c_str());
+        std::string w = exception::name("out_of_range", id) + what_arg;
+        return out_of_range(id, w.c_str());
     }
 
   private:
-    out_of_range(int id_, const char* what_arg)
-        : exception(id_, what_arg)
+    out_of_range(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg)
     {}
 };
 
@@ -345,15 +345,15 @@ json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "valu
 class other_error : public exception
 {
   public:
-    static other_error create(int id_, const std::string& what_arg)
+    static other_error create(int id, const std::string& what_arg)
     {
-        std::string w = exception::name("other_error", id_) + what_arg;
-        return other_error(id_, w.c_str());
+        std::string w = exception::name("other_error", id) + what_arg;
+        return other_error(id, w.c_str());
     }
 
   private:
-    other_error(int id_, const char* what_arg)
-        : exception(id_, what_arg)
+    other_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg)
     {}
 };
 
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index a51f77e7..c7243ca4 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -116,7 +116,7 @@ namespace detail
 
 Extension of std::exception objects with a member @a id for exception ids.
 
-@note To have nothrow-copy-constructible exceptions, we inherit from
+@note To have nothrow-copy-constructible exceptions, we internally use
       std::runtime_error which can cope with arbitrary-length error messages.
       Intermediate strings are built with static functions and then passed to
       the actual constructor.
@@ -136,16 +136,17 @@ class exception : public std::exception
     const int id;
 
   protected:
-    exception(int id_, const char* what_arg)
+    exception(int id_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
         : id(id_), m(what_arg)
     {}
 
-    static std::string name(const std::string& ename, int id_)
+    static std::string name(const std::string& ename, int id)
     {
-        return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
+        return "[json.exception." + ename + "." + std::to_string(id) + "] ";
     }
 
   private:
+    /// an exception object as storage for error messages
     std::runtime_error m;
 };
 
@@ -189,18 +190,18 @@ class parse_error : public exception
   public:
     /*!
     @brief create a parse error exception
-    @param[in] id_        the id of the exception
+    @param[in] id         the id of the exception
     @param[in] byte_      the byte index where the error occured (or 0 if
                           the position cannot be determined)
     @param[in] what_arg   the explanatory string
     @return parse_error object
     */
-    static parse_error create(int id_, size_t byte_, const std::string& what_arg)
+    static parse_error create(int id, size_t byte_, const std::string& what_arg)
     {
-        std::string w = exception::name("parse_error", id_) + "parse error" +
+        std::string w = exception::name("parse_error", id) + "parse error" +
                         (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
                         ": " + what_arg;
-        return parse_error(id_, byte_, w.c_str());
+        return parse_error(id, byte_, w.c_str());
     }
 
     /*!
@@ -216,9 +217,8 @@ class parse_error : public exception
     const size_t byte;
 
   private:
-    parse_error(int id_, size_t byte_, const char* what_arg)
-        : exception(id_, what_arg),
-          byte(byte_)
+    parse_error(int id, size_t byte_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg), byte(byte_)
     {}
 };
 
@@ -249,15 +249,15 @@ json.exception.invalid_iterator.214 | cannot get value | Cannot get value for it
 class invalid_iterator : public exception
 {
   public:
-    static invalid_iterator create(int id_, const std::string& what_arg)
+    static invalid_iterator create(int id, const std::string& what_arg)
     {
-        std::string w = exception::name("invalid_iterator", id_) + what_arg;
-        return invalid_iterator(id_, w.c_str());
+        std::string w = exception::name("invalid_iterator", id) + what_arg;
+        return invalid_iterator(id, w.c_str());
     }
 
   private:
-    invalid_iterator(int id_, const char* what_arg)
-        : exception(id_, what_arg)
+    invalid_iterator(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg)
     {}
 };
 
@@ -288,15 +288,15 @@ json.exception.type_error.315 | values in object must be primitive | The @ref un
 class type_error : public exception
 {
   public:
-    static type_error create(int id_, const std::string& what_arg)
+    static type_error create(int id, const std::string& what_arg)
     {
-        std::string w = exception::name("type_error", id_) + what_arg;
-        return type_error(id_, w.c_str());
+        std::string w = exception::name("type_error", id) + what_arg;
+        return type_error(id, w.c_str());
     }
 
   private:
-    type_error(int id_, const char* what_arg)
-        : exception(id_, what_arg)
+    type_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg)
     {}
 };
 
@@ -319,15 +319,15 @@ json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed n
 class out_of_range : public exception
 {
   public:
-    static out_of_range create(int id_, const std::string& what_arg)
+    static out_of_range create(int id, const std::string& what_arg)
     {
-        std::string w = exception::name("out_of_range", id_) + what_arg;
-        return out_of_range(id_, w.c_str());
+        std::string w = exception::name("out_of_range", id) + what_arg;
+        return out_of_range(id, w.c_str());
     }
 
   private:
-    out_of_range(int id_, const char* what_arg)
-        : exception(id_, what_arg)
+    out_of_range(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg)
     {}
 };
 
@@ -345,15 +345,15 @@ json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "valu
 class other_error : public exception
 {
   public:
-    static other_error create(int id_, const std::string& what_arg)
+    static other_error create(int id, const std::string& what_arg)
     {
-        std::string w = exception::name("other_error", id_) + what_arg;
-        return other_error(id_, w.c_str());
+        std::string w = exception::name("other_error", id) + what_arg;
+        return other_error(id, w.c_str());
     }
 
   private:
-    other_error(int id_, const char* what_arg)
-        : exception(id_, what_arg)
+    other_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+        : exception(id, what_arg)
     {}
 };
 
diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp
index f2fbc8e6..8269574e 100644
--- a/test/src/unit-noexcept.cpp
+++ b/test/src/unit-noexcept.cpp
@@ -62,15 +62,16 @@ TEST_CASE("runtime checks")
 {
     SECTION("nothrow-copy-constructible exceptions")
     {
-        // for ERR60-CPP (https://github.com/nlohmann/json/issues/531)
-        if (std::is_nothrow_copy_constructible<std::runtime_error>::value)
-        {
-            CHECK(std::is_nothrow_copy_constructible<json::exception>::value);
-            CHECK(std::is_nothrow_copy_constructible<json::parse_error>::value);
-            CHECK(std::is_nothrow_copy_constructible<json::invalid_iterator>::value);
-            CHECK(std::is_nothrow_copy_constructible<json::type_error>::value);
-            CHECK(std::is_nothrow_copy_constructible<json::out_of_range>::value);
-            CHECK(std::is_nothrow_copy_constructible<json::other_error>::value);
-        }
+        // for ERR60-CPP (https://github.com/nlohmann/json/issues/531):
+        // Exceptions should be nothrow-copy-constructible. However, compilers
+        // treat std::runtime_exception differently in this regard. Therefore,
+        // we can only demand nothrow-copy-constructibility for our exceptions
+        // if std::runtime_exception is.
+        CHECK(std::is_nothrow_copy_constructible<json::exception>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
+        CHECK(std::is_nothrow_copy_constructible<json::parse_error>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
+        CHECK(std::is_nothrow_copy_constructible<json::invalid_iterator>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
+        CHECK(std::is_nothrow_copy_constructible<json::type_error>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
+        CHECK(std::is_nothrow_copy_constructible<json::out_of_range>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
+        CHECK(std::is_nothrow_copy_constructible<json::other_error>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
     }
 }

From 333619430601807e2807b5d63eae831f435e7d00 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sun, 26 Mar 2017 12:23:10 +0200
Subject: [PATCH 7/7] :hammer: reverted changes that led to Travis failures

---
 src/json.hpp      | 14 +++++++-------
 src/json.hpp.re2c | 12 ++++++------
 2 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index 522c601d..cae427ef 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -136,7 +136,7 @@ class exception : public std::exception
     const int id;
 
   protected:
-    exception(int id_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    exception(int id_, const char* what_arg)
         : id(id_), m(what_arg)
     {}
 
@@ -185,7 +185,7 @@ json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last
 
 @since version 3.0.0
 */
-class parse_error : private exception
+class parse_error : public exception
 {
   public:
     /*!
@@ -217,7 +217,7 @@ class parse_error : private exception
     const size_t byte;
 
   private:
-    parse_error(int id, size_t byte_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    parse_error(int id, size_t byte_, const char* what_arg)
         : exception(id, what_arg), byte(byte_)
     {}
 };
@@ -256,7 +256,7 @@ class invalid_iterator : public exception
     }
 
   private:
-    invalid_iterator(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    invalid_iterator(int id, const char* what_arg)
         : exception(id, what_arg)
     {}
 };
@@ -295,7 +295,7 @@ class type_error : public exception
     }
 
   private:
-    type_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    type_error(int id, const char* what_arg)
         : exception(id, what_arg)
     {}
 };
@@ -326,7 +326,7 @@ class out_of_range : public exception
     }
 
   private:
-    out_of_range(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    out_of_range(int id, const char* what_arg)
         : exception(id, what_arg)
     {}
 };
@@ -352,7 +352,7 @@ class other_error : public exception
     }
 
   private:
-    other_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    other_error(int id, const char* what_arg)
         : exception(id, what_arg)
     {}
 };
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index c7243ca4..3aabece5 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -136,7 +136,7 @@ class exception : public std::exception
     const int id;
 
   protected:
-    exception(int id_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    exception(int id_, const char* what_arg)
         : id(id_), m(what_arg)
     {}
 
@@ -217,7 +217,7 @@ class parse_error : public exception
     const size_t byte;
 
   private:
-    parse_error(int id, size_t byte_, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    parse_error(int id, size_t byte_, const char* what_arg)
         : exception(id, what_arg), byte(byte_)
     {}
 };
@@ -256,7 +256,7 @@ class invalid_iterator : public exception
     }
 
   private:
-    invalid_iterator(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    invalid_iterator(int id, const char* what_arg)
         : exception(id, what_arg)
     {}
 };
@@ -295,7 +295,7 @@ class type_error : public exception
     }
 
   private:
-    type_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    type_error(int id, const char* what_arg)
         : exception(id, what_arg)
     {}
 };
@@ -326,7 +326,7 @@ class out_of_range : public exception
     }
 
   private:
-    out_of_range(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    out_of_range(int id, const char* what_arg)
         : exception(id, what_arg)
     {}
 };
@@ -352,7 +352,7 @@ class other_error : public exception
     }
 
   private:
-    other_error(int id, const char* what_arg) noexcept(noexcept(std::runtime_error(what_arg)))
+    other_error(int id, const char* what_arg)
         : exception(id, what_arg)
     {}
 };