From 5a2b8a274d55db8b2a4000b2db57beb8f89a4b39 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 28 Aug 2026 19:09:51 +0000 Subject: [PATCH] Do not require the container iterators to be nothrow move constructible iter_impl declared its defaulted move operations noexcept. The exception specification a defaulted function gets implicitly follows from its members, here internal_iterator, which holds the object and array iterators. libstdc++ gives std::deque's iterator a user-provided copy constructor without noexcept before version 11, so the implicit specification is noexcept(false) and does not match the declared one. That deletes the function -- and with g++ 4.8, which predates CWG 1778, it is an error outright: error: function 'iter_impl >::iter_impl( iter_impl&&)' defaulted on its first declaration with an exception-specification that differs from the implicit declaration So std::deque, which this branch documents as a usable array type, could not be used with an older standard library. Leaving the specification to be computed cannot mismatch; iteration_proxy_value already spells out the same condition next door. The default configuration is unaffected: json::iterator, json::const_iterator and ordered_json::iterator stay nothrow move constructible and move assignable, which the test now checks so it cannot regress unnoticed. Signed-off-by: Niels Lohmann --- include/nlohmann/detail/iterators/iter_impl.hpp | 9 +++++++-- single_include/nlohmann/json.hpp | 9 +++++++-- tests/src/unit-custom-array-type.cpp | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index 44448611a..22f3ffc39 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -88,8 +88,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci iter_impl() = default; ~iter_impl() = default; - iter_impl(iter_impl&&) noexcept = default; - iter_impl& operator=(iter_impl&&) noexcept = default; + // the exception specification is left to be computed rather than declared: + // an array or object type whose iterator is not nothrow move constructible + // (std::deque's is not before libstdc++ 11) would make a declared noexcept + // differ from the implicit one, which deletes the function -- and is an + // error outright with older compilers + iter_impl(iter_impl&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations) + iter_impl& operator=(iter_impl&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations) /*! @brief constructor for a given JSON instance diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 84b6d1b12..73c9d4ec2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -14764,8 +14764,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci iter_impl() = default; ~iter_impl() = default; - iter_impl(iter_impl&&) noexcept = default; - iter_impl& operator=(iter_impl&&) noexcept = default; + // the exception specification is left to be computed rather than declared: + // an array or object type whose iterator is not nothrow move constructible + // (std::deque's is not before libstdc++ 11) would make a declared noexcept + // differ from the implicit one, which deletes the function -- and is an + // error outright with older compilers + iter_impl(iter_impl&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations) + iter_impl& operator=(iter_impl&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations) /*! @brief constructor for a given JSON instance diff --git a/tests/src/unit-custom-array-type.cpp b/tests/src/unit-custom-array-type.cpp index ddfdd04cb..1e25de188 100644 --- a/tests/src/unit-custom-array-type.cpp +++ b/tests/src/unit-custom-array-type.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace @@ -40,6 +41,19 @@ using no_at_json = nlohmann::basic_json; TEST_CASE("array type without capacity()") { + SECTION("the iterators of the default configuration stay nothrow movable") + { + // basic_json's iterators take their exception specification from the + // container iterators; std::deque's is not nothrow move constructible + // with older standard libraries, which must not cost the default + // configuration its noexcept + CHECK(std::is_nothrow_move_constructible::value); + CHECK(std::is_nothrow_move_assignable::value); + CHECK(std::is_nothrow_move_constructible::value); + CHECK(std::is_move_constructible::value); + CHECK(std::is_move_assignable::value); + } + SECTION("adding elements") { deque_json j = deque_json::array();