4 ms·
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 n
by nly 2y ago
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).
- amluto 2y agoI think the comment means: std::variant<int&, etc> does not work well.
- nly 2y agoJust use std::variant<int*, ...> References in C++ are just sugary pointers.
- robmccoll 2y agoReferences have one important property over pointers. They cannot be null.
- tstack 2y agoThey most certainly can be null as can “this”.
- cma 2y agoconst ref lifetime extension is important to, or operator overloading wouldn't be workable.
- gpderetta 2y agoReference_wrapper then.
- LowLevelMahn 2y agothey easily can :) void test(int& y){} int main() { int* x = nullptr; test(*x); }
- josefx 2y agoThere 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.
- TeMPOraL 2y agoIn the same way, an `enum class` variable's value being outside of the set defined in the `enum class` is also a fuckup.
- gpderetta 2y agono, deferencing a null ptr is UB. An enum class outside the declared values is perfectly valid. 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.
- account42 2y agoAnd you can also open /proc/self/mem in a Rust program and overwrite whatever you want, including pointers. So?
- Thiez 2y agoOne of those cases happens accidentally all the time (in more complex variants than the motivating example you responded to), the other never happens except on purpose. It's like complaining guard rails are pointless because people being launched with catapults might still fly over them and plunge to their deaths.
- tlhand 2y agostd::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 { ... };