Compare commits

..
Author SHA1 Message Date
Niels Lohmann 2dc43e8bf8 Merge branch 'develop' into claude/fix-issue-2649-738aba
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 20:58:11 +02:00
Niels Lohmann e0eb3e7490 Construct explicitly in get_to() and to_json(std::optional)
With JSON_USE_IMPLICIT_CONVERSIONS=0 the conversion from a basic_json
with a different string type is now explicit, but two library paths
still assigned such a value implicitly and failed to compile inside the
library:

- get_to() with a basic_json target (the #2175 overload) did
  `v = *this`, so json(42).get_to(alt_json&) broke although
  get<alt_json>() works.
- to_json(BasicJsonType&, const std::optional<T>&) is constrained on
  std::is_constructible (which accepts the explicit constructor) but did
  `j = *opt`, so converting a std::optional<alt_json> into a json broke.

Both now construct the value explicitly, as get_impl() already does.

Also replace static_cast<bool>(JSON_USE_IMPLICIT_CONVERSIONS) with a
comparison: clang-tidy's modernize-use-bool-literals rejected the cast
of the integer literal the macro expands to, failing ci_clang_tidy.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 18:31:22 +02:00
Niels Lohmann 3ae4d7c45e Make cross-string-type basic_json conversion explicit without implicit conversions
The converting constructor from another basic_json specialization was
always implicit, so a value with a different string_t (std::wstring, a
string with a custom allocator, ...) silently converted into a temporary,
e.g. when passed to a function taking const nlohmann::json&. Such
conversions do not produce correct values (#3425), and
JSON_USE_IMPLICIT_CONVERSIONS=0 did not catch them.

When JSON_USE_IMPLICIT_CONVERSIONS is 0, the constructor is now explicit
if the string types differ. Specializations sharing a string type (json
and ordered_json, different serializers or object maps) stay implicitly
convertible, so the NLOHMANN_DEFINE_TYPE_* macros keep working with
nested json members. get<BasicJsonType>() constructs explicitly and
works in both modes.

Fixes #2649.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 16:32:26 +02:00
9 changed files with 158 additions and 70 deletions
+11 -1
View File
@@ -272,6 +272,15 @@ basic_json(basic_json&& other) noexcept;
When used without parentheses around an empty initializer list, `basic_json()` is called instead of this When used without parentheses around an empty initializer list, `basic_json()` is called instead of this
function, yielding the JSON `#!json null` value. function, yielding the JSON `#!json null` value.
- Overload 4:
!!! info "Implicit conversion"
The conversion is implicit unless [`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md)
is defined to `0` and `BasicJsonType::string_t` differs from `string_t`. In that case, the constructor is
`explicit`, so a JSON value with a different string type is no longer silently converted, for example when it is
passed to a function taking `#!cpp const json&`. Write `#!cpp json(other)` or `#!cpp other.get<json>()` instead.
- Overload 7: - Overload 7:
!!! info "Preconditions" !!! info "Preconditions"
@@ -420,7 +429,8 @@ basic_json(basic_json&& other) noexcept;
1. Since version 1.0.0. 1. Since version 1.0.0.
2. Since version 1.0.0. 2. Since version 1.0.0.
3. Since version 2.1.0. 3. Since version 2.1.0.
4. Since version 3.2.0. 4. Since version 3.2.0. Explicit for different string types if `JSON_USE_IMPLICIT_CONVERSIONS` is `0` since
version 3.13.0.
5. Since version 1.0.0. 5. Since version 1.0.0.
6. Since version 1.0.0. 6. Since version 1.0.0.
7. Since version 1.0.0. 7. Since version 1.0.0.
+3 -11
View File
@@ -86,9 +86,6 @@ Strong exception safety: if an exception occurs, the original value stays intact
- Throws [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) if an array index in the passed - Throws [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) if an array index in the passed
JSON pointer `ptr` exceeds the range of `size_type` (e.g., on 32-bit platforms). JSON pointer `ptr` exceeds the range of `size_type` (e.g., on 32-bit platforms).
For the **const** version, an object key or array index in `ptr` that does not exist is not reported by an
exception, but is undefined behavior (see the notes below). Use [`at`](at.md) for checked access.
## Complexity ## Complexity
1. Constant if `idx` is in the range of the array. Otherwise, linear in `idx - size()`. 1. Constant if `idx` is in the range of the array. Otherwise, linear in `idx - size()`.
@@ -103,12 +100,9 @@ Strong exception safety: if an exception occurs, the original value stays intact
The following cases apply to the **const** overloads; the non-const overloads instead insert the missing element The following cases apply to the **const** overloads; the non-const overloads instead insert the missing element
(see the notes below). (see the notes below).
1. If the element at index `idx` does not exist, the behavior is undefined and is **guarded by a 1. If the element at index `idx` does not exist, the behavior is undefined.
[runtime assertion](../../features/assertions.md)**!
2. If the element with key `key` does not exist, the behavior is undefined and is **guarded by a 2. If the element with key `key` does not exist, the behavior is undefined and is **guarded by a
[runtime assertion](../../features/assertions.md)**! [runtime assertion](../../features/assertions.md)**!
3. If the JSON pointer `ptr` refers to an object key or an array index that does not exist, the behavior is
undefined and is **guarded by a [runtime assertion](../../features/assertions.md)**!
1. The non-const version may add values: If `idx` is beyond the range of the array (i.e., `idx >= size()`), then the 1. The non-const version may add values: If `idx` is beyond the range of the array (i.e., `idx >= size()`), then the
array is silently filled up with `#!json null` values to make `idx` a valid reference to the last stored element. In array is silently filled up with `#!json null` values to make `idx` a valid reference to the last stored element. In
@@ -263,11 +257,9 @@ Strong exception safety: if an exception occurs, the original value stays intact
## Version history ## Version history
1. Added in version 1.0.0. A missing index in the const version is guarded by a runtime assertion since version 1. Added in version 1.0.0.
3.13.0.
2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3) 2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3)
in version 3.11.0. in version 3.11.0.
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as 3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions. already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions.
4. Added in version 2.0.0. A missing array index in the const version is guarded by a runtime assertion since 4. Added in version 2.0.0.
version 3.13.0.
@@ -5,7 +5,9 @@
``` ```
When defined to `0`, implicit conversions are switched off. By default, implicit conversions are switched on. The When defined to `0`, implicit conversions are switched off. By default, implicit conversions are switched on. The
value directly affects [`operator ValueType`](../basic_json/operator_ValueType.md). value directly affects [`operator ValueType`](../basic_json/operator_ValueType.md) and the
[converting constructor](../basic_json/basic_json.md) from a `basic_json` specialization with a different string
type (overload 4).
## Default definition ## Default definition
@@ -57,6 +59,25 @@ By default, implicit conversions are enabled.
auto s = j.get<std::string>(); auto s = j.get<std::string>();
``` ```
??? example "Conversion between `basic_json` specializations"
A `basic_json` specialization with a different string type is also no longer converted implicitly when
`JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`:
```cpp
using wjson = nlohmann::basic_json<std::map, std::vector, std::wstring>;
void load(const nlohmann::json& j);
wjson wj = /* ... */;
load(wj); // error: no implicit conversion
load(nlohmann::json(wj)); // OK: explicit conversion
load(wj.get<nlohmann::json>()); // OK: explicit conversion
```
Specializations that share the same string type, such as `json` and `ordered_json`, remain implicitly
convertible.
## See also ## See also
- [**operator ValueType**](../basic_json/operator_ValueType.md) - get a value (implicit) - [**operator ValueType**](../basic_json/operator_ValueType.md) - get a value (implicit)
@@ -66,3 +87,4 @@ By default, implicit conversions are enabled.
## Version history ## Version history
- Added in version 3.9.0. - Added in version 3.9.0.
- Also affects the conversion between `basic_json` specializations with different string types since version 3.13.0.
+7 -31
View File
@@ -16,15 +16,14 @@ before including the `json.hpp` header.
## Function with runtime assertions ## Function with runtime assertions
### Unchecked access to a const value ### Unchecked object access to a const value
Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for arrays and objects. Whereas Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for objects. Whereas a missing
a missing element is added in the case of non-const values, accessing a const value with a missing object key or an key is added in the case of non-const objects, accessing a const object with a missing key is undefined behavior (think
invalid array index is undefined behavior (think of a dereferenced null pointer) and yields a runtime assertion. This of a dereferenced null pointer) and yields a runtime assertion.
also applies to a [JSON pointer](json_pointer.md) that refers to a missing key or an invalid index.
If you are not sure whether an element exists, use checked access with the [`at` function](../api/basic_json/at.md) If you are not sure whether an element in an object exists, use checked access with the
or call the [`contains` function](../api/basic_json/contains.md) before. [`at` function](../api/basic_json/at.md) or call the [`contains` function](../api/basic_json/contains.md) before.
See also the documentation on [element access](element_access/index.md). See also the documentation on [element access](element_access/index.md).
@@ -47,30 +46,7 @@ See also the documentation on [element access](element_access/index.md).
Output: Output:
``` ```
Assertion failed: (it != m_data.m_value.object->end()), function operator[], file json.hpp, line 28795. Assertion failed: (m_value.object->find(key) != m_value.object->end()), function operator[], file json.hpp, line 2144.
```
??? example "Example 2: Invalid array index in a JSON pointer"
The following code will trigger an assertion at runtime:
```cpp
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using namespace nlohmann::literals;
int main()
{
const json j = {{"array", {1, 2, 3}}};
auto v = j["/array/5"_json_pointer];
}
```
Output:
```
Assertion failed: (idx < m_data.m_value.array->size()), function operator[], file json.hpp, line 28758.
``` ```
### Constructing from an uninitialized iterator range ### Constructing from an uninitialized iterator range
@@ -291,7 +291,9 @@ void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept
{ {
if (opt.has_value()) if (opt.has_value())
{ {
j = *opt; // explicit construction, as the conversion from a basic_json with a different
// string type is explicit if JSON_USE_IMPLICIT_CONVERSIONS is 0 (#2649)
j = BasicJsonType(*opt);
} }
else else
{ {
+2 -8
View File
@@ -535,10 +535,6 @@ class json_pointer
@return const reference to the JSON value pointed to by the JSON @return const reference to the JSON value pointed to by the JSON
pointer pointer
@pre Every object key and array index the pointer refers to exists.
Like the const operator[] for keys and indices, a missing one is
undefined behavior, guarded by a runtime assertion.
@throw parse_error.106 if an array index begins with '0' @throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number @throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used @throw out_of_range.402 if the array index '-' is used
@@ -553,8 +549,7 @@ class json_pointer
{ {
case detail::value_t::object: case detail::value_t::object:
{ {
// use unchecked object access; the const operator[] // use unchecked object access
// asserts that the key exists
ptr = &ptr->operator[](reference_token); ptr = &ptr->operator[](reference_token);
break; break;
} }
@@ -567,8 +562,7 @@ class json_pointer
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
} }
// use unchecked array access; the const operator[] // use unchecked array access
// asserts that the index exists
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
break; break;
} }
+34 -4
View File
@@ -1605,12 +1605,42 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
assert_invariant(); assert_invariant();
} }
private:
/// whether a basic_json specialization can be converted implicitly into this one;
/// with JSON_USE_IMPLICIT_CONVERSIONS set to 0, this is only the case if both share
/// the same string type (see https://github.com/nlohmann/json/issues/2649)
template<typename BasicJsonType>
using is_implicitly_convertible_basic_json = std::integral_constant < bool,
(JSON_USE_IMPLICIT_CONVERSIONS != 0)
|| std::is_same<typename BasicJsonType::string_t, string_t>::value >;
/// tag to select the constructor that performs the conversion from another basic_json specialization
struct convert_basic_json_tag {};
public:
/// @brief create a JSON value from an existing one /// @brief create a JSON value from an existing one
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template < typename BasicJsonType, template < typename BasicJsonType,
detail::enable_if_t < detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 > detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value
&& is_implicitly_convertible_basic_json<BasicJsonType>::value, int > = 0 >
basic_json(const BasicJsonType& val) basic_json(const BasicJsonType& val)
: basic_json(val, convert_basic_json_tag{})
{}
/// @brief create a JSON value from an existing one
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template < typename BasicJsonType,
detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value
&& !is_implicitly_convertible_basic_json<BasicJsonType>::value, int > = 0 >
explicit basic_json(const BasicJsonType& val)
: basic_json(val, convert_basic_json_tag{})
{}
private:
template<typename BasicJsonType>
basic_json(const BasicJsonType& val, convert_basic_json_tag /*unused*/)
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
: start_position(val.start_pos()), : start_position(val.start_pos()),
end_position(val.end_pos()) end_position(val.end_pos())
@@ -1666,6 +1696,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
assert_invariant(); assert_invariant();
} }
public:
/// @brief create a container (array or object) from an initializer list /// @brief create a container (array or object) from an initializer list
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
basic_json(initializer_list_t init, basic_json(initializer_list_t init,
@@ -2432,7 +2463,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
int > = 0 > int > = 0 >
BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
{ {
return *this; return BasicJsonType(*this);
} }
/*! /*!
@@ -2571,7 +2602,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
int> = 0> int> = 0>
ValueType & get_to(ValueType& v) const ValueType & get_to(ValueType& v) const
{ {
v = *this; v = ValueType(*this);
return v; return v;
} }
@@ -2866,7 +2897,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for arrays // const operator[] only works for arrays
if (JSON_HEDLEY_LIKELY(is_array())) if (JSON_HEDLEY_LIKELY(is_array()))
{ {
JSON_ASSERT(idx < m_data.m_value.array->size());
return m_data.m_value.array->operator[](idx); return m_data.m_value.array->operator[](idx);
} }
+39 -13
View File
@@ -6820,7 +6820,9 @@ void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept
{ {
if (opt.has_value()) if (opt.has_value())
{ {
j = *opt; // explicit construction, as the conversion from a basic_json with a different
// string type is explicit if JSON_USE_IMPLICIT_CONVERSIONS is 0 (#2649)
j = BasicJsonType(*opt);
} }
else else
{ {
@@ -19179,10 +19181,6 @@ class json_pointer
@return const reference to the JSON value pointed to by the JSON @return const reference to the JSON value pointed to by the JSON
pointer pointer
@pre Every object key and array index the pointer refers to exists.
Like the const operator[] for keys and indices, a missing one is
undefined behavior, guarded by a runtime assertion.
@throw parse_error.106 if an array index begins with '0' @throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number @throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used @throw out_of_range.402 if the array index '-' is used
@@ -19197,8 +19195,7 @@ class json_pointer
{ {
case detail::value_t::object: case detail::value_t::object:
{ {
// use unchecked object access; the const operator[] // use unchecked object access
// asserts that the key exists
ptr = &ptr->operator[](reference_token); ptr = &ptr->operator[](reference_token);
break; break;
} }
@@ -19211,8 +19208,7 @@ class json_pointer
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
} }
// use unchecked array access; the const operator[] // use unchecked array access
// asserts that the index exists
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token)); ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
break; break;
} }
@@ -27494,12 +27490,42 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
assert_invariant(); assert_invariant();
} }
private:
/// whether a basic_json specialization can be converted implicitly into this one;
/// with JSON_USE_IMPLICIT_CONVERSIONS set to 0, this is only the case if both share
/// the same string type (see https://github.com/nlohmann/json/issues/2649)
template<typename BasicJsonType>
using is_implicitly_convertible_basic_json = std::integral_constant < bool,
(JSON_USE_IMPLICIT_CONVERSIONS != 0)
|| std::is_same<typename BasicJsonType::string_t, string_t>::value >;
/// tag to select the constructor that performs the conversion from another basic_json specialization
struct convert_basic_json_tag {};
public:
/// @brief create a JSON value from an existing one /// @brief create a JSON value from an existing one
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template < typename BasicJsonType, template < typename BasicJsonType,
detail::enable_if_t < detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 > detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value
&& is_implicitly_convertible_basic_json<BasicJsonType>::value, int > = 0 >
basic_json(const BasicJsonType& val) basic_json(const BasicJsonType& val)
: basic_json(val, convert_basic_json_tag{})
{}
/// @brief create a JSON value from an existing one
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template < typename BasicJsonType,
detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value
&& !is_implicitly_convertible_basic_json<BasicJsonType>::value, int > = 0 >
explicit basic_json(const BasicJsonType& val)
: basic_json(val, convert_basic_json_tag{})
{}
private:
template<typename BasicJsonType>
basic_json(const BasicJsonType& val, convert_basic_json_tag /*unused*/)
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
: start_position(val.start_pos()), : start_position(val.start_pos()),
end_position(val.end_pos()) end_position(val.end_pos())
@@ -27555,6 +27581,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
assert_invariant(); assert_invariant();
} }
public:
/// @brief create a container (array or object) from an initializer list /// @brief create a container (array or object) from an initializer list
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
basic_json(initializer_list_t init, basic_json(initializer_list_t init,
@@ -28321,7 +28348,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
int > = 0 > int > = 0 >
BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
{ {
return *this; return BasicJsonType(*this);
} }
/*! /*!
@@ -28460,7 +28487,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
int> = 0> int> = 0>
ValueType & get_to(ValueType& v) const ValueType & get_to(ValueType& v) const
{ {
v = *this; v = ValueType(*this);
return v; return v;
} }
@@ -28755,7 +28782,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for arrays // const operator[] only works for arrays
if (JSON_HEDLEY_LIKELY(is_array())) if (JSON_HEDLEY_LIKELY(is_array()))
{ {
JSON_ASSERT(idx < m_data.m_value.array->size());
return m_data.m_value.array->operator[](idx); return m_data.m_value.array->operator[](idx);
} }
+36
View File
@@ -13,6 +13,7 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -374,4 +375,39 @@ TEST_CASE("alternative string type")
const auto j2 = j.flatten(); const auto j2 = j.flatten();
CHECK(j2.dump() == R"({"/foo/0":"bar","/foo/1":"baz"})"); CHECK(j2.dump() == R"({"/foo/0":"bar","/foo/1":"baz"})");
} }
SECTION("conversion between basic_json specializations (#2649)")
{
// explicit conversions are always possible
CHECK(std::is_constructible<nlohmann::json, alt_json>::value);
CHECK(std::is_constructible<alt_json, nlohmann::json>::value);
CHECK(std::is_constructible<nlohmann::json, nlohmann::ordered_json>::value);
CHECK(std::is_constructible<nlohmann::ordered_json, nlohmann::json>::value);
// specializations with the same string type are implicitly convertible
CHECK(std::is_convertible<nlohmann::ordered_json, nlohmann::json>::value);
CHECK(std::is_convertible<nlohmann::json, nlohmann::ordered_json>::value);
// specializations with different string types are only implicitly convertible
// if implicit conversions are enabled
#if JSON_USE_IMPLICIT_CONVERSIONS
CHECK(std::is_convertible<alt_json, nlohmann::json>::value);
CHECK(std::is_convertible<nlohmann::json, alt_json>::value);
#else
CHECK_FALSE(std::is_convertible<alt_json, nlohmann::json>::value);
CHECK_FALSE(std::is_convertible<nlohmann::json, alt_json>::value);
#endif
// get<BasicJsonType>() works in either case
const nlohmann::json j = {{"foo", 1}, {"bar", true}};
CHECK(j.get<nlohmann::ordered_json>() == nlohmann::ordered_json(j));
// (only a number is converted here, as objects and strings are affected by #3425)
CHECK(nlohmann::json(42).get<alt_json>() == 42);
CHECK(alt_json(nlohmann::json(42)) == 42);
// get_to() also works in either case
alt_json a;
nlohmann::json(42).get_to(a);
CHECK(a == 42);
}
} }