Skip to content

Suggestion to improve value() accessors with respect to move semantics #1275

@murderotica

Description

@murderotica

Here's a couple suggestions that I think might be worth adding to improve the usage of value() function:

  1. Use "universal reference" && instead of const& for default_value to lift a problem that const ValueType& default_value inhibits move when default_value is temporary/r-value:

Before:

template<class ValueType, typename std::enable_if<
                 std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
    {
        // at only works for objects
        if (JSON_LIKELY(is_object()))
        {
            // if key is found, return value and given default value otherwise
            const auto it = find(key);
            if (it != end())
            {
                return *it;
            }

            return default_value;
        }

        JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
}

After

template<class ValueType, typename std::enable_if<
                 std::is_convertible<basic_json_t, detail::uncvref_t<ValueType>>::value, int>::type = 0>
    detail::uncvref_t<ValueType> value(const typename object_t::key_type& key, ValueType&& default_value) const &
    {
        // at only works for objects
        if (JSON_LIKELY(is_object()))
        {
            // if key is found, return value and given default value otherwise
            const auto it = find(key);
            if (it != end())
            {
                return *it;
            }
            return std::forward<ValueType>(default_value);
        }

        JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
    }

This will allow this:

json j{{"x", "test"}};
std::string defval = "default value";
auto val = j.value("y", std::move(defval) /* or a temporary constructed in-place */);

// defval is now empty (moved-from) and val contains "default value"
  1. Provide r-value reference overload for value() to allow moving from JSON's values when JSON is temporary/r-value:
template<class ValueType, typename std::enable_if<
                 std::is_convertible<basic_json_t, detail::uncvref_t<ValueType>>::value, int>::type = 0>
    detail::uncvref_t<ValueType> value(const typename object_t::key_type& key, ValueType&& default_value) &&
    {
        // at only works for objects
        if (JSON_LIKELY(is_object()))
        {
            // if key is found, return value and given default value otherwise
            const auto it = find(key);
            if (it != end())
            {
                return std::move(it->template get_ref<ValueType&>());
            }

            return std::forward<ValueType>(default_value);
        }

        JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
    }

This will make possible the following:

json j{{"x", "123"}};
auto val = std::move(j).value("x", std::string{"whatever"});

// j["x"] in now empty (moved-from) and val contains "123"

Metadata

Metadata

Assignees

No one assigned

    Labels

    state: stalethe issue has not been updated in a while and will be closed automatically soon unless it is updated

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions