Move elements when converting rvalue containers to JSON

Add to_json overloads for rvalue array-like and object-like containers
that build the array/object from move iterators, so that
json j = std::move(vec) moves the elements instead of copying them.
std::vector<std::string> (100k x 40 chars): 4.7 ms -> 2.3 ms;
std::map<std::string, std::string>: -18 % (keys are const and still copied).

array_t/object_t, std::vector<bool>, std::filesystem::path and
std::u8string keep their dedicated overloads. Lvalues and const rvalues
are copied as before.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-14 22:25:04 +02:00
1 parent 4e294f4b18
commit cc2dae641d
5 files changed
+429 -1

No files matched your search

@@ -167,6 +167,17 @@ struct external_constructor<value_t::array>
j.replace_value(value_t::array, value);
}
/// an rvalue container: move its elements
template<typename BasicJsonType, typename CompatibleArrayType>
static void construct_moving(BasicJsonType& j, CompatibleArrayType& arr)
{
using std::begin;
using std::end;
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(std::make_move_iterator(begin(arr)), std::make_move_iterator(end(arr))));
j.replace_value(value_t::array, value);
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
{
@@ -217,6 +228,17 @@ struct external_constructor<value_t::object>
j.replace_value(value_t::object, value);
}
/// an rvalue container: move its values
template<typename BasicJsonType, typename CompatibleObjectType>
static void construct_moving(BasicJsonType& j, CompatibleObjectType& obj)
{
using std::begin;
using std::end;
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(std::make_move_iterator(begin(obj)), std::make_move_iterator(end(obj))));
j.replace_value(value_t::object, value);
}
template < typename BasicJsonType, typename CompatibleObjectType,
enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
@@ -354,6 +376,45 @@ inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
external_constructor<value_t::array>::construct(j, arr);
}
#if defined(__cpp_lib_char8_t)
// std::basic_string<char8_t> is converted to a string by a dedicated to_json overload
template<typename T>
struct is_u8string : std::false_type {};
template<typename Tr, typename Allocator>
struct is_u8string<std::basic_string<char8_t, Tr, Allocator>> : std::true_type {};
#endif
// rvalue containers: their elements are moved instead of copied
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < !std::is_lvalue_reference<CompatibleArrayType>::value
&& !std::is_const<CompatibleArrayType>::value
&& !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value
&& !std::is_same<CompatibleArrayType, std::vector<bool>>::value
&& !is_basic_json<CompatibleArrayType>::value
&& is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value
&& !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value
&& !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value
&& !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value
&& !is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
&& !std::is_same<CompatibleArrayType, std_fs::path>::value // has its own to_json
#endif
#if defined(__cpp_lib_char8_t)
&& !is_u8string<CompatibleArrayType>::value // has its own to_json
#endif
#if JSON_HAS_RANGE_VIEW_CONVERSION
&& !is_compatible_range_view<CompatibleArrayType>::value
#endif
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
&& !is_enum_keyed_map<CompatibleArrayType>::value
#endif
, int > = 0 >
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr)
{
external_constructor<value_t::array>::construct_moving(j, arr);
}
#if JSON_HAS_RANGE_VIEW_CONVERSION
template < typename BasicJsonType, typename T,
enable_if_t < is_compatible_range_view<std::remove_cvref_t<T>>::value
@@ -427,6 +488,21 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
}
#endif
template < typename BasicJsonType, typename CompatibleObjectType,
enable_if_t < !std::is_lvalue_reference<CompatibleObjectType>::value
&& !std::is_const<CompatibleObjectType>::value
&& !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value
&& is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value
&& !is_basic_json<CompatibleObjectType>::value
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
&& !is_enum_keyed_map<CompatibleObjectType>::value
#endif
, int > = 0 >
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj)
{
external_constructor<value_t::object>::construct_moving(j, obj);
}
template<typename BasicJsonType>
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{