I think you're deliberately making it more verbose than it needs to be. The class definition could be condensed into:
class Expression {
enum Tag { litteral, symbol, funcall, conditional,
while_loop, sequence } _tag;
union {
Value *litteral;
std::string *symbol; // or a true symbol?
std::vector<Expression> *composite;
} _val;
};
And you could probably use templates or macros to autogenerate most of those constructors/destructors automatically.
> I think you're deliberately making it more verbose than it needs to be.
I swear I am not. This is will be production code, for which I earn my salary. Your code is terser, but still fundamentally the same as mine. It's still unsafe (we could access the wrong union member), and it still has to work around incomplete definitions and the lack of trivial constructors (I wouldn't use pointers otherwise).
Automating my constructors and destructors would be amazing. Unfortunately, I don't know how to do it —not without writing over a hundred lines of cryptic boilerplate first.
The ML example was missing the second half of the ML niceness - efficient pattern matching at syntax levels which makes for a really nice and understandable code, especially when writing complex compound clauses.
"...could probably use templates or macros to autogenerate.."
... which kind of highlights that ML example even more. In Ocaml or F# that original definition would have been more or less enough to start writing nice, efficient parsing code.
The sum types are not the only thing, IMO, that ML-family has going for it. As for example F# contains pairs and lists as syntax level constructs (rather than library add-ons) lot of code can be written at a really high level before optimization - in the same language - if it's needed.