Skip to content

fix: treat single-element brace-init as copy/move instead of wrapping in array (#5074)#5090

Open
ssam18 wants to merge 2 commits intonlohmann:developfrom
ssam18:fix/issue-5074-brace-init-semantics
Open

fix: treat single-element brace-init as copy/move instead of wrapping in array (#5074)#5090
ssam18 wants to merge 2 commits intonlohmann:developfrom
ssam18:fix/issue-5074-brace-init-semantics

Conversation

@ssam18
Copy link
Contributor

@ssam18 ssam18 commented Mar 3, 2026

Summary

When a value is passed using single-element brace initialization e.g., json j{someObj} or via a by-value parameter void foo(json j) called as foo({someObj}). C++ always prefers the initializer_list constructor over the copy/move constructor. This caused the value to be silently wrapped in a single-element array.

This bug was previously compiler-dependent: GCC wrapped the value while Clang did not. As of Clang 20, Clang now matches GCC behavior, making this a universal regression.

Validation

All existing unit tests pass with the fix applied.
Fixes the issue #5074.

@gregmarr
Copy link
Contributor

gregmarr commented Mar 3, 2026

See the first entry in the FAQ. https://json.nlohmann.me/home/faq/#brace-initialization-yields-arrays
Any change in this behavior would be a regression for all other supported compilers.

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

🔴 Amalgamation check failed! 🔴

The source code has not been amalgamated. @ssam18
Please read and follow the Contribution Guidelines.

ssam18 added 2 commits March 2, 2026 21:04
When passing a json value using brace initialization with a single element
(e.g., `json j{someObj}` or `foo({someJson})`), C++ always prefers the
initializer_list constructor over the copy/move constructor. This caused
the value to be unexpectedly wrapped in a single-element array.

This bug was previously compiler-dependent (GCC wrapped, Clang did not),
but Clang 20 started matching GCC behavior, making it a universal issue.

Fix: In the initializer_list constructor, when type deduction is enabled
and the list has exactly one element, copy/move it directly instead of
creating a single-element array.

Before:
  json obj = {{"key", 1}};
  json j{obj};   // -> [{"key":1}]  (wrong: array)
  foo({obj});    // -> [{"key":1}]  (wrong: array)

After:
  json j{obj};   // -> {"key":1}   (correct: copy)
  foo({obj});    // -> {"key":1}   (correct: copy)

To explicitly create a single-element array, use json::array({value}).

Fixes the issue nlohmann#5074

Signed-off-by: Samaresh Kumar Singh <ssam3003@gmail.com>
- Add missing comment from include/nlohmann/json.hpp explaining the
  single-element brace-init fix (issue nlohmann#5074)
- Fix extra 4-space indentation in embedded json_fwd.hpp section

Regenerated by running: make amalgamate

Signed-off-by: Samaresh Kumar Singh <ssam3003@gmail.com>
@ssam18 ssam18 force-pushed the fix/issue-5074-brace-init-semantics branch from bee5b16 to 6ba0665 Compare March 3, 2026 03:05
@github-actions
Copy link

github-actions bot commented Mar 3, 2026

🔴 Amalgamation check failed! 🔴

The source code has not been amalgamated. @ssam18
Please read and follow the Contribution Guidelines.

1 similar comment
@github-actions
Copy link

github-actions bot commented Mar 3, 2026

🔴 Amalgamation check failed! 🔴

The source code has not been amalgamated. @ssam18
Please read and follow the Contribution Guidelines.

@nlohmann
Copy link
Owner

There is currently no way to fix this behavior without breaking existing code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants