Compare commits

..
Author SHA1 Message Date
Niels Lohmann 8c266a10a8 Merge branch 'develop' into claude/pr-3899-merge-requirements-e1932e
Conflicts:
- tests/src/unit-custom-base-class.cpp: include list; kept this PR's
  <type_traits>/<utility> and develop's <vector>

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 20:30:00 +02:00
Niels Lohmann 4ab78e8092 Merge branch 'develop' into claude/pr-3899-merge-requirements-e1932e
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 22:22:42 +02:00
Niels Lohmann 10c0d41f7c Silence clang-tidy for the hidden type_name() in the base class test
ci_clang_tidy failed with readability-convert-member-functions-to-static
on base_class_with_hidden_members::type_name(). It must stay a
non-static member: the test shows that it is hidden by the non-static
basic_json::type_name() and reachable through as_base_class().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 18:01:10 +02:00
Niels Lohmann de261db5ae Merge branch 'develop' into claude/pr-3899-merge-requirements-e1932e
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 20:58:14 +02:00
Niels Lohmann 013923dc89 Add as_base_class to the docset search index
New public members get an entry in docs/docset/docSet.sql (as done for
to_bon8/from_bon8 in #2998). Without it, the Dash/Zeal docset built from
the documentation cannot find basic_json::as_base_class.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 18:19:44 +02:00
Niels Lohmann 82601da6ec Merge branch 'develop' into as_base_class
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 16:56:59 +02:00
Niels LohmannandRaphael Grimm 1cd636bd61 Add basic_json::as_base_class and document name conflicts with custom base classes
Members of basic_json hide members of a custom base class with the same
name, and future releases may add members that hide ones accessible
today. Document this in json_base_class_t and add as_base_class() to
reach hidden members without spelling out the cast.

Also make json_base_class_t a public member type. It was documented
since 3.12.0, but declared private, so users could not name it.

Supersedes #3899.

Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 14:53:50 +02:00
10 changed files with 248 additions and 154 deletions
+1
View File
@@ -16,6 +16,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json', 'Class', 'api/ba
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array', 'Function', 'api/basic_json/array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array_t', 'Type', 'api/basic_json/array_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::as_base_class', 'Method', 'api/basic_json/as_base_class/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::at', 'Method', 'api/basic_json/at/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::back', 'Method', 'api/basic_json/back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Constructor', 'api/basic_json/basic_json/index.html');
@@ -0,0 +1,53 @@
# <small>nlohmann::basic_json::</small>as_base_class
```cpp
json_base_class_t& as_base_class() noexcept;
const json_base_class_t& as_base_class() const noexcept;
```
Returns a reference to this object as its custom base class [`json_base_class_t`](json_base_class_t.md). No copy is
made.
Since `basic_json` derives from `json_base_class_t`, a member of `basic_json` hides any member of the custom base class
with the same name. This function makes such hidden members accessible again.
## Return value
reference to this object as [`json_base_class_t`](json_base_class_t.md)
## Exception safety
No-throw guarantee: this function never throws exceptions.
## Complexity
Constant.
## Notes
The function is equivalent to `static_cast<json_base_class_t&>(j)` (or `static_cast<const json_base_class_t&>(j)`).
## Examples
??? example
The example shows how to use `as_base_class` to access members of the custom base class that are hidden by members
of `basic_json`.
```cpp
--8<-- "examples/as_base_class.cpp"
```
Output:
```json
--8<-- "examples/as_base_class.output"
```
## See also
- [json_base_class_t](json_base_class_t.md) - type of the custom base class
## Version history
- Added in version 3.13.0.
+1
View File
@@ -200,6 +200,7 @@ Direct access to the stored value of a JSON value.
- [**get_ref**](get_ref.md) - get a reference value
- [**operator ValueType**](operator_ValueType.md) - get a value
- [**get_binary**](get_binary.md) - get a binary value
- [**as_base_class**](as_base_class.md) - access the custom base class
### Element access
@@ -27,6 +27,18 @@ A `CustomBaseClass` with non-static data members forfeits `basic_json`'s
[standard layout](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType) guarantee. See
[Template Parameter Requirements](../../features/types/template_parameters.md#custombaseclass).
#### Name conflicts
Since `basic_json` derives from `CustomBaseClass`, members of `basic_json` hide members of `CustomBaseClass` with the
same name. Hidden members remain accessible via [`as_base_class`](as_base_class.md) or by casting the value to
`json_base_class_t`.
!!! warning "Avoid generic member names"
Future versions of the library may add members to `basic_json` that hide members of `CustomBaseClass` that are
accessible today. To reduce the risk of such conflicts, avoid generic names for the members of `CustomBaseClass`,
for instance by using a distinctive prefix.
## Examples
??? example
@@ -43,6 +55,11 @@ A `CustomBaseClass` with non-static data members forfeits `basic_json`'s
--8<-- "examples/json_base_class_t.output"
```
## See also
- [as_base_class](as_base_class.md) - access the custom base class
## Version history
- Added in version 3.12.0.
- Made a public member type in version 3.13.0; it was private before, so it could not be named outside the class.
@@ -0,0 +1,41 @@
#include <iostream>
#include <nlohmann/json.hpp>
class base_class_with_hidden_members
{
public:
const char* type_name() const noexcept
{
return "my_type_name";
}
std::size_t size() const noexcept
{
return 42;
}
};
using json = nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
base_class_with_hidden_members
>;
int main()
{
json j = {1, 2, 3};
// the members of basic_json hide the members of the base class
std::cout << j.type_name() << ' ' << j.size() << '\n';
// access the hidden members of the base class
std::cout << j.as_base_class().type_name() << ' ' << j.as_base_class().size() << '\n';
}
@@ -0,0 +1,2 @@
array 3
my_type_name 42
+1
View File
@@ -114,6 +114,7 @@ nav:
- 'accept': api/basic_json/accept.md
- 'array': api/basic_json/array.md
- 'array_t': api/basic_json/array_t.md
- 'as_base_class': api/basic_json/as_base_class.md
- 'at': api/basic_json/at.md
- 'back': api/basic_json/back.md
- 'begin': api/basic_json/begin.md
+38 -49
View File
@@ -155,7 +155,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// workaround type for MSVC
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
JSON_PRIVATE_UNLESS_TESTED:
// convenience aliases for types residing in namespace detail;
@@ -215,6 +214,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// how to encode BJData
using bjdata_version_t = detail::bjdata_version_t;
/// base class used to inject custom functionality into each instance of basic_json
/// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
/// helper type for initializer lists of basic_json values
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
@@ -1004,38 +1006,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using copy_scratch_value_t = std::pair<typename object_t::key_type, basic_json>;
using copy_scratch_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
/// @brief tag selecting the constructor below; used only to build the
/// elements of a deep copy (@ref copy_array_level, @ref copy_object_level)
struct copy_construct_tag {};
public:
/*!
@brief construct a null value whose base class - and, with @ref
JSON_DIAGNOSTIC_POSITIONS, positions - are copied from @a src
Copy-constructing @ref json_base_class_t here, rather than default-
constructing the element and assigning its base class afterwards, means
that copying a @ref basic_json only ever requires a copy-constructible
base class, and never a move-assignable one as well.
@note this constructor has to be public: @ref copy_array_level and
@ref copy_object_level reach it through @ref array_t's or @ref
object_t's own emplace_back(), which constructs the element from
outside @ref basic_json and so cannot call a private constructor.
@ref copy_construct_tag is private, though, and nothing in the
public interface hands out a value of it, so outside code can still
never name it to call this constructor itself.
*/
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
: json_base_class_t(src)
#if JSON_DIAGNOSTIC_POSITIONS
, start_position(src.start_position)
, end_position(src.end_position)
#endif
/// @brief copy everything of @a src into @a dst but its type and value
static void copy_metadata(const basic_json& src, basic_json& dst)
{
}
// a custom base class is only required to be copy-constructible and
// move-assignable, so the copy has to go through a temporary
static_cast<json_base_class_t&>(dst) = json_base_class_t(static_cast<const json_base_class_t&>(src));
private:
#if JSON_DIAGNOSTIC_POSITIONS
dst.start_position = src.start_position;
dst.end_position = src.end_position;
#endif
}
/*!
@brief copy the value of @a src into @a dst, which must not be structured
@@ -1099,11 +1081,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
/*!
@brief finish the copy @a dst of @a src that a @ref copy_construct_tag
constructor started, other than the children of an object or array
@brief copy everything of @a src into the null value @a dst but the children
@a dst already has @a src's base class and, with @ref
JSON_DIAGNOSTIC_POSITIONS, positions; only its value is still missing.
Objects and arrays are not copied here; they are appended to @a worklist to
be created later by @ref copy_iteratively. Until that happens, @a dst remains
a null value, so that a partially built copy can be destroyed at any point
@@ -1111,6 +1090,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
*/
static void copy_shallow(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
{
copy_metadata(src, dst);
if (src.m_data.m_type == value_t::object || src.m_data.m_type == value_t::array)
{
// defer: dst stays a null value until its container exists
@@ -1131,19 +1112,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
const array_t& src_array = *src.m_data.m_value.array;
// create all elements up front: growing the array afterwards could
// invalidate the pointers that are handed to the worklist; resize()
// rather than the fill constructor, because not every array type
// provides the latter (e.g., ones without a matching allocator-aware
// fill constructor)
dst.m_data.m_value.array = create<array_t>();
// only now that the array exists may dst stop being a null value
dst.m_data.m_type = value_t::array;
// create every element - its base class already copy-constructed from
// its counterpart in src, via the copy_construct_tag constructor -
// before any of their addresses are handed to worklist below: growing
// the array while that is going on could reallocate it and invalidate
// addresses taken from an earlier iteration
for (const auto& src_element : src_array)
{
dst.m_data.m_value.array->emplace_back(copy_construct_tag{}, src_element);
}
dst.m_data.m_value.array->resize(src_array.size());
auto dst_it = dst.m_data.m_value.array->begin();
for (auto src_it = src_array.cbegin(); src_it != src_array.cend(); ++src_it, ++dst_it)
@@ -1161,14 +1138,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// build the complete key skeleton and hand it to the object's range
// constructor: adding the keys one by one would be quadratic for object
// types that are backed by a vector, such as nlohmann::ordered_map; each
// value's base class is already copy-constructed from its counterpart
// in src, via the copy_construct_tag constructor
// types that are backed by a vector, such as nlohmann::ordered_map
scratch.clear();
scratch.reserve(src_object.size());
for (const auto& element : src_object)
{
scratch.emplace_back(element.first, basic_json(copy_construct_tag{}, element.second));
scratch.emplace_back(element.first, basic_json());
}
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
@@ -2718,6 +2693,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return *get_ptr<const binary_t*>();
}
/// @brief access the custom base class
/// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
json_base_class_t& as_base_class() noexcept
{
return static_cast<json_base_class_t&>(*this);
}
/// @brief access the custom base class
/// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
const json_base_class_t& as_base_class() const noexcept
{
return static_cast<const json_base_class_t&>(*this);
}
/// @}
////////////////////
+38 -49
View File
@@ -27082,7 +27082,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// workaround type for MSVC
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
JSON_PRIVATE_UNLESS_TESTED:
// convenience aliases for types residing in namespace detail;
@@ -27142,6 +27141,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// how to encode BJData
using bjdata_version_t = detail::bjdata_version_t;
/// base class used to inject custom functionality into each instance of basic_json
/// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
/// helper type for initializer lists of basic_json values
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
@@ -27931,38 +27933,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using copy_scratch_value_t = std::pair<typename object_t::key_type, basic_json>;
using copy_scratch_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
/// @brief tag selecting the constructor below; used only to build the
/// elements of a deep copy (@ref copy_array_level, @ref copy_object_level)
struct copy_construct_tag {};
public:
/*!
@brief construct a null value whose base class - and, with @ref
JSON_DIAGNOSTIC_POSITIONS, positions - are copied from @a src
Copy-constructing @ref json_base_class_t here, rather than default-
constructing the element and assigning its base class afterwards, means
that copying a @ref basic_json only ever requires a copy-constructible
base class, and never a move-assignable one as well.
@note this constructor has to be public: @ref copy_array_level and
@ref copy_object_level reach it through @ref array_t's or @ref
object_t's own emplace_back(), which constructs the element from
outside @ref basic_json and so cannot call a private constructor.
@ref copy_construct_tag is private, though, and nothing in the
public interface hands out a value of it, so outside code can still
never name it to call this constructor itself.
*/
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
: json_base_class_t(src)
#if JSON_DIAGNOSTIC_POSITIONS
, start_position(src.start_position)
, end_position(src.end_position)
#endif
/// @brief copy everything of @a src into @a dst but its type and value
static void copy_metadata(const basic_json& src, basic_json& dst)
{
}
// a custom base class is only required to be copy-constructible and
// move-assignable, so the copy has to go through a temporary
static_cast<json_base_class_t&>(dst) = json_base_class_t(static_cast<const json_base_class_t&>(src));
private:
#if JSON_DIAGNOSTIC_POSITIONS
dst.start_position = src.start_position;
dst.end_position = src.end_position;
#endif
}
/*!
@brief copy the value of @a src into @a dst, which must not be structured
@@ -28026,11 +28008,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
/*!
@brief finish the copy @a dst of @a src that a @ref copy_construct_tag
constructor started, other than the children of an object or array
@brief copy everything of @a src into the null value @a dst but the children
@a dst already has @a src's base class and, with @ref
JSON_DIAGNOSTIC_POSITIONS, positions; only its value is still missing.
Objects and arrays are not copied here; they are appended to @a worklist to
be created later by @ref copy_iteratively. Until that happens, @a dst remains
a null value, so that a partially built copy can be destroyed at any point
@@ -28038,6 +28017,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
*/
static void copy_shallow(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
{
copy_metadata(src, dst);
if (src.m_data.m_type == value_t::object || src.m_data.m_type == value_t::array)
{
// defer: dst stays a null value until its container exists
@@ -28058,19 +28039,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
const array_t& src_array = *src.m_data.m_value.array;
// create all elements up front: growing the array afterwards could
// invalidate the pointers that are handed to the worklist; resize()
// rather than the fill constructor, because not every array type
// provides the latter (e.g., ones without a matching allocator-aware
// fill constructor)
dst.m_data.m_value.array = create<array_t>();
// only now that the array exists may dst stop being a null value
dst.m_data.m_type = value_t::array;
// create every element - its base class already copy-constructed from
// its counterpart in src, via the copy_construct_tag constructor -
// before any of their addresses are handed to worklist below: growing
// the array while that is going on could reallocate it and invalidate
// addresses taken from an earlier iteration
for (const auto& src_element : src_array)
{
dst.m_data.m_value.array->emplace_back(copy_construct_tag{}, src_element);
}
dst.m_data.m_value.array->resize(src_array.size());
auto dst_it = dst.m_data.m_value.array->begin();
for (auto src_it = src_array.cbegin(); src_it != src_array.cend(); ++src_it, ++dst_it)
@@ -28088,14 +28065,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// build the complete key skeleton and hand it to the object's range
// constructor: adding the keys one by one would be quadratic for object
// types that are backed by a vector, such as nlohmann::ordered_map; each
// value's base class is already copy-constructed from its counterpart
// in src, via the copy_construct_tag constructor
// types that are backed by a vector, such as nlohmann::ordered_map
scratch.clear();
scratch.reserve(src_object.size());
for (const auto& element : src_object)
{
scratch.emplace_back(element.first, basic_json(copy_construct_tag{}, element.second));
scratch.emplace_back(element.first, basic_json());
}
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
@@ -29645,6 +29620,20 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return *get_ptr<const binary_t*>();
}
/// @brief access the custom base class
/// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
json_base_class_t& as_base_class() noexcept
{
return static_cast<json_base_class_t&>(*this);
}
/// @brief access the custom base class
/// @sa https://json.nlohmann.me/api/basic_json/as_base_class/
const json_base_class_t& as_base_class() const noexcept
{
return static_cast<const json_base_class_t&>(*this);
}
/// @}
////////////////////
+56 -56
View File
@@ -10,6 +10,8 @@
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "doctest_compatibility.h"
@@ -406,72 +408,70 @@ TEST_CASE("JSON Visit Node")
CHECK(expected.empty());
}
// A custom base class with a const member: copy-constructible (initializing a
// const member works fine), but not copy-/move-assignable (assigning one does
// not). Used to check that copy construction never requires more than that.
struct const_member_base
// Test accessing members of a custom base class that are hidden by members of nlohmann::basic_json
class base_class_with_hidden_members
{
const int id = 7; // NOLINT(misc-non-private-member-variables-in-classes)
public:
const char* type_name() const noexcept // NOLINT(readability-convert-member-functions-to-static)
{
return "custom type_name";
}
std::size_t size() const noexcept
{
return m_size;
}
std::size_t m_size = 42;
};
using json_with_const_base = nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
const_member_base
>;
using json_with_hidden_base_members =
nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
base_class_with_hidden_members
>;
// build an array nested @a depth levels deep, with the innermost value 1;
// every level is constructed (never assigned), since const_member_base does
// not support assignment
static json_with_const_base make_nested_array(std::size_t depth)
TEST_CASE("JSON Node as_base_class")
{
if (depth == 0)
{
return json_with_const_base(1);
}
return json_with_const_base::array({make_nested_array(depth - 1)});
}
using json = json_with_hidden_base_members;
TEST_CASE("Regression test for issue #5674 - copy construction must not require an assignable base class")
{
SECTION("depth 0")
{
// as in the original bug report: copy construction only, no assignment
const json_with_const_base j = {1, 2};
const json_with_const_base copy = j; // NOLINT(performance-unnecessary-copy-initialization)
static_assert(std::is_same<decltype(std::declval<json&>().as_base_class()), json::json_base_class_t&>::value, "");
static_assert(std::is_same<decltype(std::declval<const json&>().as_base_class()), const json::json_base_class_t&>::value, "");
static_assert(noexcept(std::declval<json&>().as_base_class()), "");
static_assert(noexcept(std::declval<const json&>().as_base_class()), "");
CHECK(copy.size() == 2);
CHECK(copy.id == 7);
SECTION("non-const")
{
json j = {1, 2, 3};
CHECK(std::string(j.type_name()) == "array");
CHECK(j.size() == 3);
CHECK(std::string(j.as_base_class().type_name()) == "custom type_name");
CHECK(j.as_base_class().size() == 42);
CHECK(&j.as_base_class() == &static_cast<json::json_base_class_t&>(j));
j.as_base_class().m_size = 7;
CHECK(j.as_base_class().size() == 7);
CHECK(j.size() == 3);
}
SECTION("nested deeper than the copy constructor's descent bound")
SECTION("const")
{
// beyond nesting_depth_limit() (128) levels, the copy constructor
// copies without the call stack (copy_iteratively / copy_array_level),
// which used to assign the base class of every element it created
const std::size_t depth = 300;
const json j = {1, 2, 3};
const json_with_const_base j = make_nested_array(depth);
const json_with_const_base copy = j; // NOLINT(performance-unnecessary-copy-initialization)
const json_with_const_base* c = &copy;
for (std::size_t level = 0; level <= depth; ++level)
{
CAPTURE(level)
REQUIRE(c->id == 7);
if (level < depth)
{
c = &c->at(0);
}
}
CHECK(*c == 1);
CHECK(std::string(j.type_name()) == "array");
CHECK(j.size() == 3);
CHECK(std::string(j.as_base_class().type_name()) == "custom type_name");
CHECK(j.as_base_class().size() == 42);
CHECK(&j.as_base_class() == &static_cast<const json::json_base_class_t&>(j));
}
}