Hacker News new | ask | show | jobs
by fgkhax 681 days ago
Yes, you read about std::variant on a blog and think that it is a sum type. Then you try it out and realize that it's a thin (type-safe) wrapper over tagged unions that is at least three times slower and has about 5 unreadable alternatives that replace simple switch statements.

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.

3 comments

> and obviously cannot work once you realize that std::variant is just syntax sugar over a tagged union

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.

A few code snippets of what you see as weaknesses of std::variant may be appropriate, as I couldn't figure out your complaint. Assigning to a variant taken by non-const& works fine for me.

I personally would have liked to see recursive variant types and multi-visitation (as supported by boost::variant).

std::variant is not a true algebraic data type, since the individual element constructors do not construct the variant type automatically. Compare to OCaml, written in a verbose and unidiomatic way that is similar to C++:

  # 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 { ... };
I think the comment means:

    std::variant<int&, etc>
does not work well.
Just use

    std::variant<int*, ...>
References in C++ are just sugary pointers.
References have one important property over pointers. They cannot be null.
Reference_wrapper then.
they easily can :)

void test(int& y){}

int main() { int* x = nullptr; test(*x); }

There is a difference between an API promissing that a value wont be null and a buggy program setting a null where it should not. A reference is only null if someone fucked up. As a programmer you can usually rely on a reference not being null and you couldn't do anything about it if it was anyway within the constraints of the language.
And you can also open /proc/self/mem in a Rust program and overwrite whatever you want, including pointers. So?
const ref lifetime extension is important to, or operator overloading wouldn't be workable.
They most certainly can be null as can “this”.
Only after dereferencing a null pointer, which would be the actual problem, not the fact that some reference or this is null.
You cannot have a null reference unless you invoke undefined behavior first.
Ohh, and to make the use of a variant to look like pattern match over type you need to copy paste some template magic.

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