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

* fix: treat single-element brace-init as copy/move

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 #5074

Signed-off-by: Samaresh Kumar Singh <ssam3003@gmail.com>

* fix: regenerate amalgamated single_include/nlohmann/json.hpp

- Add missing comment from include/nlohmann/json.hpp explaining the
  single-element brace-init fix (issue #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>

* Revert brace-init semantics change and fix amalgamation

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 <ssam3003@gmail.com>

* Fix: add JSON_BRACE_INIT_COPY_SEMANTICS opt-in macro for issue #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 <ssam3003@gmail.com>

* docs: add dedicated macro page and CI test target for JSON_BRACE_INIT_COPY_SEMANTICS

Signed-off-by: Samaresh Kumar Singh <ssam3003@gmail.com>

* fix: remove compiler-dependent assertions from #5074 regression test

Signed-off-by: Samaresh Kumar Singh <ssam3003@gmail.com>

* fix: use defined() guard for JSON_BRACE_INIT_COPY_SEMANTICS to satisfy -Wundef

Signed-off-by: Samaresh Kumar Singh <ssam3003@gmail.com>

* docs: fix section name in json_brace_init_copy_semantics.md to pass style check

Signed-off-by: Samaresh Kumar Singh <ssam3003@gmail.com>

* docs: move Default definition section before Notes to fix style check order

Signed-off-by: Samaresh Kumar Singh <ssam3003@gmail.com>

---------

Signed-off-by: Samaresh Kumar Singh <ssam3003@gmail.com>
This commit is contained in:
SamareshSingh
2026-05-08 08:20:24 +02:00
committed by GitHub
parent fd17b0889e
commit 62f3b41b30
9 changed files with 196 additions and 0 deletions
+15
View File
@@ -212,6 +212,21 @@ add_custom_target(ci_test_legacycomparison
COMMENT "Compile and test with legacy discarded value comparison enabled"
)
###############################################################################
# Enable brace-init copy semantics.
###############################################################################
add_custom_target(ci_test_brace_init_copy_semantics
COMMAND ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_BuildTests=ON -DJSON_FastTests=ON
-DCMAKE_CXX_FLAGS=-DJSON_BRACE_INIT_COPY_SEMANTICS=1
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_brace_init_copy_semantics
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_brace_init_copy_semantics
COMMAND cd ${PROJECT_BINARY_DIR}/build_brace_init_copy_semantics && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
COMMENT "Compile and test with brace-init copy semantics enabled"
)
###############################################################################
# Disable global UDLs.
###############################################################################