Is this safe?
Depends on what you consider "safe". Your code does not involve undefined behavior or such things, but it can definitely lead to unexpected results. Example:
Value v1 = std::int64_t{std::numeric_limits<std::int64_t>::min()};
Value v2 = std::uint64_t{42};
// Isn't v1 < v2? This assertion will fire...
assert(compare(v1, v2));
The issue here is that your catch-all visitor (the generic lambda) will not distinguish between types and happily compares a signed integral value with an unsigned one. The former is converted to the latter, yielding a large value and the comparison returns true.
If not how can it be accomplished safely?
You can add additional overloads to the visitor function object. Have a look at the overloaded
machinery here. This way, you do distinguish between the type that the variant holds whenever the comparison requires it to do the right thing (i.e., return an expectable result).