Then you find out that members of a "variant" are not really variant members but just the individual types that can be assigned to a union. For example, assigning to a non-const reference does not work (and obviously cannot work once you realize that std::variant is just syntax sugar over a tagged union).
Most of these new additions since C++11 are just leaky abstractions and wrappers.
https://schneide.blog/2018/01/11/c17-the-two-line-visitor-ex...
variants are a such disappointment at every step of trying to use them
I personally would have liked to see recursive variant types and multi-visitation (as supported by boost::variant).
std::variant<int&, etc>
does not work well. std::variant<int*, ...>
References in C++ are just sugary pointers.void test(int& y){}
int main() { int* x = nullptr; test(*x); }
You could design a language feature where integer to enum is checked, but that's not enum.
Enum classes already add scoping, forbid implicit conversions and allow explicit underlying types. Those are pure extensions. Making undeclared values invalid or UB would be very surprising to people used to normal enums.
What I have seen is automatic variables escaping their scope as a reference, which rust protects against. And is also much more dangerous, because dereferencing a nullptr is defined behavior on most platforms
# type foo = Int of { n : int } | Float of { f : float };;
type foo = Int of { n : int; } | Float of { f : float; }
# Int { n = 10 };;
- : foo = Int {n = 10}
# let r = ref (Int { n = 10 });;
val r : foo ref = {contents = Int {n = 10}}
Notice that the constructor Int { n = 10 } automatically produces a foo type and assigning to a mutable ref works.The same in C++, using assignment to a pointer to avoid the lvalue ref error that is irrelevant to this discussion:
#include <variant>
struct myint {
int n;
myint(int n) : n(n) {}
};
struct myfloat {
float f;
myfloat(float f) : f(f) {}
};
using foo = std::variant<myint, myfloat>;
int
main()
{
const foo& x = myint{10}; // works
foo *z = new myint{10}; // error: cannot convert ‘myint*’ to ‘foo*
}
As stated above, this obviously cannot work since C++ has no way of specifying a myint constructor that -- like in OCaml -- automatically produces the variant type foo.C++ would need true algebraic data types with compiler support (that would hopefully be as fast as switch statements). To be useful, they would need a nice syntax and not some hypothetical abomination like:
using foo = std::variant<myint, myfloat> where
struct myint of foo { ... };It would be easy to make it work, there isn't necessarily a strict relation between the template parameter and the actual stored object. Not having reference variant members was a conscious decision, same as optional<T&>. Hopefully this will be fixed in the future.