This is a sort-of continuation, sort-of reopening of #3130 - it's not exactly the same use case, but certain applications of items() on a json object still don't play nicely with C++20 std::ranges.
In particular, while items() can be used as an input for views like std::views::transform, that result isn't usable in situations that require a forward_iterator, such as constructing a container from the output range's iterators. See the code below for what I mean.
The compilation error suggests the view iterator's iterator_category is not defined, which is the case if the base range doesn't model forward_range.
I did some debugging using static_asserts, and found that while iteration_proxy_valuedoes meet all the requirements for a forward_iterator, its iterator_category is explicitly exposed as only std::input_iterator_tag. Simply changing that to std::forward_iterator_tag (or removing it and letting iterator_traits deduce it) in my testing makes it fully model forward_iterator, so that items() models forward_range and the view is usable in this case.
Reproduction steps
Crate a json object
Run its items() through an std::views::transform
Try to construct a container via that result's iterators, e.g. std::vector<std::string> s{view.begin(), view.end()};
Expected vs. actual results
iteration_proxy is just a wrapper around the actual iterator of json itself, which does satisfy forward_iterator, so I expect iteration_proxy_value to also satisfy forward_iterator. Instead, it only satisfies input_iterator.
Minimal code example
#include <nlohmann/json.hpp>
#include <ranges>
#include <string>
#include <vector>
int main()
{
// This works
nlohmann::json arr { 1, 2, 3 };
auto arrTransform = std::views::transform([](auto&& element){ return element.template get<int>() * 2; });
auto arrView = arr | arrTransform;
std::vector<int> arrVec{arrView.begin(), arrView.end()};
// This doesn't work
nlohmann::json obj {
{ "one", 1 },
{ "two", 2 },
{ "three", 3 }
};
auto objItems = obj.items();
auto objTransform = std::views::transform([](auto&& element){ return element.key(); });
auto objView = objItems | objTransform;
std::vector<std::string> keys{objView.begin(), objView.end()}; // Fails to compile
}
Error messages
json-range.cpp: In function ‘int main()’:
json-range.cpp:24:65: error: no matching function for call to ‘std::vector<std::__cxx11::basic_string<char>, std::allocator<std::__cxx11::basic_string<char> > >::vector(<brace-enclosed initializer list>)’
23 | std::vector<std::string> keys{objView.begin(), objView.end()}; // Fails to compile
| ^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/vector:66,
from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/functional:64,
from /usr/include/nlohmann/json.hpp:23,
from json-range.cpp:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_vector.h:707:9: note: candidate: ‘template<class _InputIterator, class> constexpr std::vector<_Tp, _Alloc>::vector(_InputIterator, _InputIterator, const allocator_type&) [with <template-parameter-2-2> = _InputIterator; _Tp = std::__cxx11::basic_string<char>; _Alloc = std::allocator<std::__cxx11::basic_string<char> >]’
707 | vector(_InputIterator __first, _InputIterator __last,
| ^~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_vector.h:707:9: note: template argument deduction/substitution failed:
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_algobase.h:65,
from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/algorithm:60,
from /usr/include/nlohmann/json.hpp:21:
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_iterator_base_types.h: In substitution of ‘template<class _InIter> using std::_RequireInputIter = std::__enable_if_t<std::is_convertible<typename std::iterator_traits< <template-parameter-1-1> >::iterator_category, std::input_iterator_tag>::value> [with _InIter = std::ranges::transform_view<std::ranges::ref_view<nlohmann::json_abi_v3_11_3::detail::iteration_proxy<nlohmann::json_abi_v3_11_3::detail::iter_impl<nlohmann::json_abi_v3_11_3::basic_json<> > > >, main()::<lambda(auto:24&&)> >::_Iterator<false>]’:
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_vector.h:705:9: required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_iterator_base_types.h:250:11: error: no type named ‘iterator_category’ in ‘struct std::iterator_traits<std::ranges::transform_view<std::ranges::ref_view<nlohmann::json_abi_v3_11_3::detail::iteration_proxy<nlohmann::json_abi_v3_11_3::detail::iter_impl<nlohmann::json_abi_v3_11_3::basic_json<> > > >, main()::<lambda(auto:24&&)> >::_Iterator<false> >’
250 | using _RequireInputIter =
| ^~~~~~~~~~~~~~~~~
Compiler and operating system
Gentoo, GCC 13.2
Library version
3.11.3
Validation
[X] The bug also occurs if the latest version from the develop branch is used.
Description
This is a sort-of continuation, sort-of reopening of #3130 - it's not exactly the same use case, but certain applications of
items()
on a json object still don't play nicely with C++20std::ranges
.In particular, while
items()
can be used as an input for views likestd::views::transform
, that result isn't usable in situations that require aforward_iterator
, such as constructing a container from the output range's iterators. See the code below for what I mean.The compilation error suggests the view iterator's
iterator_category
is not defined, which is the case if the base range doesn't modelforward_range
.I did some debugging using
static_assert
s, and found that whileiteration_proxy_value
does meet all the requirements for aforward_iterator
, itsiterator_category
is explicitly exposed as onlystd::input_iterator_tag
. Simply changing that tostd::forward_iterator_tag
(or removing it and lettingiterator_traits
deduce it) in my testing makes it fully modelforward_iterator
, so thatitems()
modelsforward_range
and the view is usable in this case.Reproduction steps
json
objectitems()
through anstd::views::transform
std::vector<std::string> s{view.begin(), view.end()};
Expected vs. actual results
iteration_proxy
is just a wrapper around the actual iterator ofjson
itself, which does satisfyforward_iterator
, so I expectiteration_proxy_value
to also satisfyforward_iterator
. Instead, it only satisfiesinput_iterator
.Minimal code example
Error messages
Compiler and operating system
Gentoo, GCC 13.2
Library version
3.11.3
Validation
develop
branch is used.