For example, it should be very helpful to equal compare a std::variant<T1, T2>
with a T1
or T2
. So far we can only compare with the same variant type.
It is an arbitrary decision by the standards committee.
Ok, not quite arbitrary. The point is you have a scale* of strictness of comparison, with points such as:
- Most-strict: Only variants can equal each other, and they need to match both in the sequence-of-alternatives (i.e. the type), the actual alternative (the index, really, since you can have multiple identical-type alternatives) and in value.
- Less-Strict: Equality of both the variant alternative, as a type and the value, but not of the sequence-of-alternatives, nor the index within that sequence (so the same value within two distinct alternatives of the same type would be equal).
- Most-relaxed: Equality of the value in the active alternative, with implicit conversion of one of the elements if relevant.
These are all valid choices. the C++ committee made the decision based on all sorts of extrinsic criteria. Try looking up the std::variant
proposal, as perhaps it says what these criteria are.
(*) - A lattice actually.
A variant may have multiple duplicates of the same type. E.g. std::variant<int, int>
.
A given instance of std::variant
compares equal to another if and only if they hold the same variant alternative and said alternatives' values compare equal.
Thus, a std::variant<int, int>
with index()
0 compares not equal to a std::variant<int, int>
with index()
1, despite the active variant alternatives being of the same type and same value.
Because of this, a generic "compare to T
" was not implemented by the standard. However, you are free to design your own overload of the comparison operators using the other helper utilities in the <variant>
header (e.g. std::holds_alternative
and std::get<T>
).
int
with std::variant<int, int>
it could compare the active member with that int
. –
Prepuce T
is not in the standard. –
Clarissa boost::variant
has been available for decades, excluding i can't use boost people. Integrating boost::variant
into std
wouldn't be necessary if C++ had a package repository and a build system. Brain dump completed. –
Prepuce boost::variant
also does not support equality-comparison with one of the alternative types. –
Absolution myvariant == static_cast<decltype(myvariant)>(myint)
to compare. What was the more useful use case to allow duplicate types? –
Prepuce variant<a,b,c>
always has alternative 1 being b
, and if alternative of var1 is not alternative of var2 they are never equal. If you go down the path of "well, I'm sure they meant variant<int, double>
when they said variant<int, int, double>
", you end up with javascript "1"==1
insanity. If you want to go further, look up "sum types" in type theory. –
Bismuthous index()
check plus a template visitor. The index()
check just being to make sure std::variant<A, B>
holding A
which is comparable to B
still fails (so as not to fly in the face of the default comparison of two variants). –
Clarissa It is an arbitrary decision by the standards committee.
Ok, not quite arbitrary. The point is you have a scale* of strictness of comparison, with points such as:
- Most-strict: Only variants can equal each other, and they need to match both in the sequence-of-alternatives (i.e. the type), the actual alternative (the index, really, since you can have multiple identical-type alternatives) and in value.
- Less-Strict: Equality of both the variant alternative, as a type and the value, but not of the sequence-of-alternatives, nor the index within that sequence (so the same value within two distinct alternatives of the same type would be equal).
- Most-relaxed: Equality of the value in the active alternative, with implicit conversion of one of the elements if relevant.
These are all valid choices. the C++ committee made the decision based on all sorts of extrinsic criteria. Try looking up the std::variant
proposal, as perhaps it says what these criteria are.
(*) - A lattice actually.
I can't answer the why part of the question but since you think it would be useful to be able to compare a std::variant<T1, T2>
with a T1
or T2
, perhaps this can help:
template<typename T, class... Types>
inline bool operator==(const T& t, const std::variant<Types...>& v) {
const T* c = std::get_if<T>(&v);
return c && *c == t; // true if v contains a T that compares equal to t
}
template<typename T, class... Types>
inline bool operator==(const std::variant<Types...>& v, const T& t) {
return t == v;
}
You can actually write this yourself! If you aren't so inclined to override equals operators (which in my opinion you shouldn't do with std
classes), we can write with templates and type_traits
the desired functionality.
#include <variant>
#include <type_traits>
template<class Variant, class T>
struct compare_variant;
template<class T, class ... Ts>
struct compare_variant<std::variant<Ts...>, T>
{
static bool apply(std::variant<Ts...> obj1, T obj2)
{
return ((std::is_same<Ts, T>::value
&& std::holds_alternative<T>(obj1)
&& obj1 == obj2)||...);
}
};
Note that this will only work if T
is a member of Ts...
. It is certainly possible to solve this for any arbitrary T
but it requires slightly more complicated template machinery. Let me know if you would like this snippet as well.
© 2022 - 2024 — McMap. All rights reserved.
std::variant<int, std::string>
to satisfy the comparison function signature. For an int, that's probably trivial. But for a string, that could mean a copy. – Clarissa