From 17362c3417f732e9878bd7e7d2b36ab103126ed1 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 30 Aug 2026 21:01:41 +0000 Subject: [PATCH] Assert the iterators' exception specification relative to the container The test pinned that nlohmann::json's iterators stay nothrow movable after iter_impl's defaulted move operations lost their declared noexcept. That is not a property of the library, though: the exception specification is now computed from the container iterators, so it holds only for standard library implementations whose iterators are themselves nothrow movable. MSVC's checked iterators before VS2017 are not -- _Iterator_base12 registers the iterator with the container's debug proxy in a copy constructor that carries no noexcept -- so the assertions fail on a Visual Studio 2015 debug build, which is the one debug configuration in the AppVeyor matrix and has no counterpart in the GitHub Actions matrix. Assert what the change actually guarantees instead: the iterators are nothrow movable exactly when the object and array iterators they are built from are. That still pins the default configuration against a silent regression, and it is true whatever the standard library provides. Signed-off-by: Niels Lohmann --- tests/src/unit-custom-array-type.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/src/unit-custom-array-type.cpp b/tests/src/unit-custom-array-type.cpp index 1e25de188..df66340a1 100644 --- a/tests/src/unit-custom-array-type.cpp +++ b/tests/src/unit-custom-array-type.cpp @@ -41,15 +41,25 @@ using no_at_json = nlohmann::basic_json; TEST_CASE("array type without capacity()") { - SECTION("the iterators of the default configuration stay nothrow movable") + SECTION("the iterators take their exception specification from the container") { - // 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); + // basic_json's iterators move exactly as the container iterators do: + // their move operations are defaulted without a declared noexcept, + // because an array or object type whose iterator is not nothrow move + // constructible would otherwise have them deleted (std::deque's is not + // with libstdc++ before 11, and neither are MSVC's debug iterators) + CHECK(std::is_nothrow_move_constructible::value == + (std::is_nothrow_move_constructible::value + && std::is_nothrow_move_constructible::value)); + CHECK(std::is_nothrow_move_assignable::value == + (std::is_nothrow_move_assignable::value + && std::is_nothrow_move_assignable::value)); + CHECK(std::is_nothrow_move_constructible::value == + (std::is_nothrow_move_constructible::value + && std::is_nothrow_move_constructible::value)); + + // and they are movable at all, which is what dropping the declared + // noexcept buys for a std::deque array CHECK(std::is_move_constructible::value); CHECK(std::is_move_assignable::value); }