Make cross-string-type basic_json conversion explicit without implicit conversions (#5591)

* 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>

* 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>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-10-04 11:46:52 +02:00
committed by GitHub
parent a212d3b2e4
commit 1df1e8a845
6 changed files with 144 additions and 10 deletions

View File

@@ -5,7 +5,9 @@
```
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
@@ -59,6 +61,25 @@ By default, implicit conversions are enabled.
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
- [**operator ValueType**](../basic_json/operator_ValueType.md) - get a value (implicit)
@@ -68,3 +89,4 @@ By default, implicit conversions are enabled.
## Version history
- Added in version 3.9.0.
- Also affects the conversion between `basic_json` specializations with different string types since version 3.13.0.