Skip to content

Commit 25e740d

Browse files
committed
fix inconsistent choice of init list constructor
On some compilers value{x} invokes initializer_list constructor, on others it is equivalent to value(x). This is very problematic, but this isn't something we can fix. On the other hand, we CAN make init list construction to be equivalent to value(x), if the size of init list is one. This commit does exactly that.
1 parent 2acdb29 commit 25e740d

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

include/boost/json/impl/value.ipp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,28 @@ value(
231231
storage_ptr sp)
232232
{
233233
if(value_ref::maybe_object(init))
234+
{
234235
::new(&obj_) object(
235236
value_ref::make_object(
236237
init, std::move(sp)));
238+
}
237239
else
238-
::new(&arr_) array(
239-
value_ref::make_array(
240-
init, std::move(sp)));
240+
{
241+
#ifndef BOOST_JSON_LEGACY_INIT_LIST_BEHAVIOR
242+
if( init.size() == 1 )
243+
{
244+
::new(&sca_) scalar();
245+
value temp = init.begin()->make_value( std::move(sp) );
246+
swap(temp);
247+
}
248+
else
249+
#endif
250+
{
251+
::new(&arr_) array(
252+
value_ref::make_array(
253+
init, std::move(sp)));
254+
}
255+
}
241256
}
242257

243258
//----------------------------------------------------------

include/boost/json/value.hpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,11 +1014,16 @@ class value
10141014

10151015
/** Construct from an initializer-list
10161016
1017-
If the initializer list consists of key/value
1018-
pairs, an @ref object is created. Otherwise
1019-
an @ref array is created. The contents of the
1020-
initializer list are copied to the newly constructed
1021-
value using the specified memory resource.
1017+
@li If the initializer list consists of key/value
1018+
pairs, an @ref object is created; otherwise,
1019+
1020+
@li if the size of the initializer list is exactly 1, the object is
1021+
constructed directly from that sole element; otherwise,
1022+
1023+
@li an @ref array is created.
1024+
1025+
The contents of the initializer list are copied to the newly
1026+
constructed value using the specified memory resource.
10221027
10231028
@par Complexity
10241029
Linear in `init.size()`.
@@ -1032,6 +1037,20 @@ class value
10321037
@param sp A pointer to the @ref memory_resource
10331038
to use. The container will acquire shared
10341039
ownership of the memory resource.
1040+
1041+
@par Note
1042+
The previous behavior of this constructor was to always
1043+
construct either an @ref object or an @ref array. In practice though,
1044+
several C++ implementations did not treat `value{x}` as a constructor
1045+
from initializer list. This effectively resulted in different behavior
1046+
on different implementations. <br>
1047+
1048+
If you need the legacy behavior define macro
1049+
`BOOST_JSON_LEGACY_INIT_LIST_BEHAVIOR` when you are building the
1050+
library. The macro and the functionality will be deprecated in the
1051+
future and then removed, so we urge you to change your code for the new
1052+
behavior as soon as possible. The simplest way to create an @ref array
1053+
with 1 element using an initializer list is via `array{x}`.
10351054
*/
10361055
BOOST_JSON_DECL
10371056
value(

test/value.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2131,6 +2131,17 @@ class value_test
21312131
void
21322132
testInitList()
21332133
{
2134+
{
2135+
value jv{};
2136+
BOOST_TEST( jv.is_null() );
2137+
}
2138+
#ifndef BOOST_JSON_LEGACY_INIT_LIST_BEHAVIOR
2139+
{
2140+
value jv{0};
2141+
BOOST_TEST( jv == 0 );
2142+
}
2143+
#endif
2144+
21342145
check_array(value{0,0,0}, 0, 0, 0);
21352146
check_array(value{false,false,false}, false, false, false);
21362147
check_array(value{false,2,false}, false, 2, false);

0 commit comments

Comments
 (0)