Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 19 additions & 34 deletions src/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8053,50 +8053,35 @@ class basic_json
}
}

/*
Use operator `const_iterator` instead of `const_iterator(const iterator&
other) noexcept` to avoid two class definitions for @ref iterator and
@ref const_iterator.

This function is only called if this class is an @ref iterator. If this
class is a @ref const_iterator this function is not called.
/*!
@note The conventional copy constructor and copy assignment are
implicitly defined.
Combined with the following converting constructor and assigment,
they support: copy from iterator to iterator,
copy from const iterator to const iterator,
and conversion from iterator to const iterator.
However conversion from const iterator to iterator is not defined.
*/
operator const_iterator() const
{
const_iterator ret;

if (m_object)
{
ret.m_object = m_object;
ret.m_it = m_it;
}

return ret;
}

/*!
@brief copy constructor
@param[in] other iterator to copy from
@brief converting constructor
@param[in] other non-const iterator to copy from
@note It is not checked whether @a other is initialized.
*/
iter_impl(const iter_impl& other) noexcept
iter_impl(const iter_impl<basic_json>& other) noexcept
: m_object(other.m_object), m_it(other.m_it)
{}

/*!
@brief copy assignment
@param[in,out] other iterator to copy from
@brief converting assignment
@param[in,out] other non-const iterator to copy from
@return const/non-const iterator
@note It is not checked whether @a other is initialized.
*/
iter_impl& operator=(iter_impl other) noexcept(
std::is_nothrow_move_constructible<pointer>::value and
std::is_nothrow_move_assignable<pointer>::value and
std::is_nothrow_move_constructible<internal_iterator>::value and
std::is_nothrow_move_assignable<internal_iterator>::value
)
{
std::swap(m_object, other.m_object);
std::swap(m_it, other.m_it);
iter_impl& operator=(const iter_impl<basic_json>& other) noexcept
{
m_object = other.m_object;
m_it = other.m_it;
return *this;
}

Expand Down Expand Up @@ -8585,7 +8570,7 @@ class basic_json
/// associated JSON instance
pointer m_object = nullptr;
/// the actual iterator of the associated instance
internal_iterator m_it = internal_iterator();
struct internal_iterator m_it = internal_iterator();
};

/*!
Expand Down
68 changes: 68 additions & 0 deletions test/src/unit-iterators1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1511,4 +1511,72 @@ TEST_CASE("iterators 1")
}
}
}

SECTION("conversion from iterator to const iterator")
{
SECTION("boolean")
{
json j = true;
json::const_iterator it = j.begin();
CHECK(it == j.cbegin());
it = j.begin();
CHECK(it == j.cbegin());
}
SECTION("string")
{
json j = "hello world";
json::const_iterator it = j.begin();
CHECK(it == j.cbegin());
it = j.begin();
CHECK(it == j.cbegin());
}
SECTION("array")
{
json j = {1, 2, 3};
json::const_iterator it = j.begin();
CHECK(it == j.cbegin());
it = j.begin();
CHECK(it == j.cbegin());
}
SECTION("object")
{
json j = {{"A", 1}, {"B", 2}, {"C", 3}};
json::const_iterator it = j.begin();
CHECK(it == j.cbegin());
it = j.begin();
CHECK(it == j.cbegin());
}
SECTION("number (integer)")
{
json j = 23;
json::const_iterator it = j.begin();
CHECK(it == j.cbegin());
it = j.begin();
CHECK(it == j.cbegin());
}
SECTION("number (unsigned)")
{
json j = 23u;
json::const_iterator it = j.begin();
CHECK(it == j.cbegin());
it = j.begin();
CHECK(it == j.cbegin());
}
SECTION("number (float)")
{
json j = 23.42;
json::const_iterator it = j.begin();
CHECK(it == j.cbegin());
it = j.begin();
CHECK(it == j.cbegin());
}
SECTION("null")
{
json j = nullptr;
json::const_iterator it = j.begin();
CHECK(it == j.cbegin());
it = j.begin();
CHECK(it == j.cbegin());
}
}
}