12

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.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
wanghan02
  • 1,227
  • 7
  • 14
  • 4
    I think c++20 just added it to the draft with the [mothership proposal](https://brevzin.github.io/cpp_proposals/118x_spaceship/p1614r0.html#was-ill-formed-now-well-formed) – Guillaume Racicot Mar 20 '19 at 18:36
  • 1
    @GuillaumeRacicot In that same link, I believe it's saying the int would be implicitly cast to `std::variant` to satisfy the comparison function signature. For an int, that's probably trivial. But for a string, that could mean a copy. – Cruz Jean Mar 20 '19 at 18:43
  • Shouldn't the compare also work in C++17? the operator== is a freestanding function. And variant has a non-explicit conversion-constructor, so variant() == T1() should convert T1 to variant and it shuould find the freestanding operator==. Actually after a check I found out that it does not work. Why is that so? – Kilian Mar 21 '19 at 22:24
  • Actually following code works: struct X { template X(T&&) {} }; bool operator==(X const&, X const&); X x(10); bool b = x==10; So it seemes to be related to operator== being a template. – Kilian Mar 21 '19 at 22:25

3 Answers3

11

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>).

Cruz Jean
  • 2,761
  • 12
  • 16
5

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;
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
4

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.

einpoklum
  • 118,144
  • 57
  • 340
  • 684