Skip to content

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

Merged
nlohmann merged 9 commits into
nlohmann:developfrom
ssam18:fix/issue-5074-brace-init-semantics
May 8, 2026
Merged

fix: treat single-element brace-init as copy/move instead of wrapping in array (#5074)#5090
nlohmann merged 9 commits into
nlohmann:developfrom
ssam18:fix/issue-5074-brace-init-semantics

Conversation

@ssam18
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown

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 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
Copy Markdown

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
Copy Markdown

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
Copy Markdown
Owner

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

@ssam18 ssam18 force-pushed the fix/issue-5074-brace-init-semantics branch from 8986a1d to 336a8f7 Compare April 2, 2026 01:10
@ssam18 ssam18 force-pushed the fix/issue-5074-brace-init-semantics branch from 6647c53 to 9563808 Compare April 2, 2026 18:33
@ssam18
Copy link
Copy Markdown
Contributor Author

ssam18 commented Apr 2, 2026

@nlohmann - Can you please take a look?

@nlohmann
Copy link
Copy Markdown
Owner

nlohmann commented Apr 2, 2026

I am not sure about this. Yet another macro adds another moving part to the library...

@gregmarr
Copy link
Copy Markdown
Contributor

gregmarr commented Apr 3, 2026

It might also need to be encoded in the namespace so you don't accidentally link against a version with the wrong behavior.

Comment thread docs/mkdocs/docs/home/faq.md
@github-actions github-actions Bot added L and removed M labels Apr 3, 2026
@ssam18 ssam18 force-pushed the fix/issue-5074-brace-init-semantics branch from 363a2ba to 58de969 Compare April 3, 2026 15:30
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 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
Copy Markdown

github-actions Bot commented Apr 3, 2026

🔴 Amalgamation check failed! 🔴

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

@ssam18 ssam18 force-pushed the fix/issue-5074-brace-init-semantics branch from 5bb8c14 to feb3dad Compare April 3, 2026 15:34
Comment thread docs/mkdocs/docs/home/faq.md
@gregmarr
Copy link
Copy Markdown
Contributor

gregmarr commented Apr 3, 2026

I am not sure about this. Yet another macro adds another moving part to the library...

Given how often this is reported as a bug, I think having a better answer than "We know, sorry, don't do that." is a good thing.

I think this is one of those things that should become the only method in 4.0 and be selectable until then.

@ssam18 ssam18 force-pushed the fix/issue-5074-brace-init-semantics branch from 8578552 to 187c763 Compare April 7, 2026 22:15
@github-actions github-actions Bot added the CMake label Apr 7, 2026
@ssam18
Copy link
Copy Markdown
Contributor Author

ssam18 commented Apr 7, 2026

@nlohmann - I have addressed your review comment. Please review

ssam18 added 9 commits April 7, 2026 19:22
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 <[email protected]>
- 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 <[email protected]>
The single-element brace-init change was a breaking change that cannot be accepted upstream. Reverted all related source, test, and doc changes, then regenerated single_include with correct indentation to pass the amalgamation CI check.

Signed-off-by: Samaresh Kumar Singh <[email protected]>
…nn#5074

Single-element brace initialization wrapping in an array cannot be fixed without breaking existing code. Added JSON_BRACE_INIT_COPY_SEMANTICS as an opt-in macro (default 0) so users can enable copy/move semantics for single-element brace init without affecting anyone relying on the current behavior.

Signed-off-by: Samaresh Kumar Singh <[email protected]>
@ssam18 ssam18 force-pushed the fix/issue-5074-brace-init-semantics branch from 06f4313 to 214e4cf Compare April 8, 2026 00:22
@ssam18 ssam18 requested a review from nlohmann April 8, 2026 01:09
@ssam18
Copy link
Copy Markdown
Contributor Author

ssam18 commented Apr 10, 2026

@nlohmann - Can we merge this PR?

@gregmarr
Copy link
Copy Markdown
Contributor

gregmarr commented May 5, 2026

@nlohmann Have all your concerns been addressed?

@ssam18
Copy link
Copy Markdown
Contributor Author

ssam18 commented May 6, 2026

@nlohmann Have all your concerns been addressed?

yes, that is true.

Copy link
Copy Markdown
Owner

@nlohmann nlohmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@nlohmann
Copy link
Copy Markdown
Owner

nlohmann commented May 8, 2026

Thanks!

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