Skip to content

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

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"

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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