Skip to content

MSVC: unexpected behaviour converting from json to a rvalue reference of a variant containing a type that can be constructed from a string #5066

@karcherm

Description

@karcherm

Description

Converting a json object to an rvalue of a variant containing a type that can be constructed from a string and json itself, will result in construction of a variant with that string-constructible type, and crash if the json object does not contain a string. This even happens on a variant containing only nlohmann::json, as that type is both "constructible from string" and "nlohmann::json itself".

I suspect this is actually an MSVC compiler bug, but a workaround in the json library would be nice, if possible.

Reproduction steps

Given

using var = std::variant<nlohmann::json>;
std::vector<var> vars;

run

vars.push_back(nlohmann::json(1));

Expected vs. actual results

Actual: the call vars.push_back(json(1)) results in an assertion failure

Expected: a variant containing a json object containing the number 1 is pushed into the vector

The expected result occurs, because the json object does not contain a string. This happens because the type var is constructible from a string, and if MSVC requires a conversion from json to var &&, it prefers the route via template<typename T> T json::operator T() const over the route of creating a temporary variant being passed to push_back.

Minimal code example

#include <variant>
#include <vector>
#include <nlohmann/json.hpp>

int main()
{
    std::vector<std::variant<nlohmann::json>> v;
    v.push_back(nlohmann::json(1));
}

Furthermore, I set up a demonstration for MSVC picking the unwanted conversion path without nlohmann::json on Compiler Explorer. I did not use the actual JSON library, because Compiler Explorer doesn't support external libraries on Windows. I tested locally using MSVC that the issue happens with the acutal JSON library in exactly the same way. See
https://godbolt.org/z/xP8r1nYT8 .

Error messages

The miminal example shown bove terminates with an assertion failure (`JSON_THROW`) with this backtrace:

nlohmann::detail::from_json<nlohman::json, std::variant<nlohmann::json>, 0>(const nlohman::json&, std::variant<nlohmann::json>&)
nlohmann::detail::from_json_fn::operator()<nlohmann::json, std::variant<nlohmann::json>&>(nlohmann::json&, std::variant<nlohmann::json>&)
nlohmann::adl_serializer<std::variant<nlohmann::json>, void>::from_json<const nlohmann::json &, std::variant<nlohmann::json>>(const nlohmann::json&, std::variant<nlohmann::json>>&)
nlohmann::json::get_impl<std::variant<nlohmann::json>, 0>(nlohmann::detail::priority_tag<0>)
nlohmann::json::get<std::variant<nlohmann::json>, std::variant<nlohmann::json>>()
nlohmann::json::operator<std::variant<nlohmann::json>, 0> std::variant<nlohmann::json>()
main()

Compiler and operating system

Visual Studio 17.6.5 running on Microsoft Window 11 25H2

Library version

3.10.5

Validation

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions