Gennadiy Rozental wrote: > > BTW, after looking at the implementation I was a bit disappointed to > > see two copies of the storage. It seems to nullify one > > important reason for using variants (space savings), and it generates > > more code than a single-storage version. I know you had some rationale > for > > that but I don't remember what it is; I hope it's in the docs along > > with some explanation of the expected size of the resulting object. > > > > Regards, > > Dave > > Double storage is needed to ensure strong guaraty for variant. I argued need > for it before and I still believe we not nesseserily need to do that (I > remember your post on the topic, that strong guaranty is a "incorrect" goal > to pursue for the variant).
Yes, it is true that double storage provides strong guarantee for variant. But that is not the point: double storage is needed to ensure even the basic guarantee! First let me acknowledge that the above rests on certain technical assumptions about variant's invariants (sorry about the tongue twister). Namely, it assumes that "never empty" is an invariant. Thus, for instance, boost::variant<int, std::string> *always* contains either an int or a std::string, and that these types maintain their own invariants. There are important reasons I have built variant on this assumption. Suppose I have a variant v1, with content of type T1, and a variant v2, also with content of type T1. Then the assignment v1 = v2 is perfectly straightforward: use T1::operator=. Obviously no double storage is needed for this case. But suppose I have a variant v3, with content of a different type (call it T3). Then the assignment v1 = v3 is far more complicated (we can't use T1::operator=) and, without double storage, far more dangerous. The single storage implementation behaves as follows: destroy v1 content copy v3 content into v1 The destruction will of course always occur because variant requires nothrow destructors from all of its bounded types. But what if the copy fails? Then the variant has no meaningful content whatsoever, since there is no guarantee we can restore the original content or, worse still, even construct new content of one of variant's bounded types. To see why this is a problem, we need to look back at the motivation for variant. Apart from efficiency, one of the significant advantages of using variant instead of boost::any is improved type safety, specifically at compile-time. Indeed, the visitation mechanism forces the user to handle every possible type of content. If we then go ahead and allow the possibility of the above "empty" state (to be distinguished from boost::empty content), we greatly complicate these semantics. Are we to say that anytime a variant is involved in a failed assign/swap operation, visitation will subsequently fail -- but only at runtime? Some may find this behavior acceptable. Personally, I'm not totally opposed, as there are alternative (albeit less elegant) options. But I think before throwing away double storage, we need to fully understand what else will leave with it. I invite (and would appreciate) feedback, as this is an important issue. Thanks, Eric P.S. Any type with a nothrow move constructor (which includes all types with a nothrow copy constructor) is not included in the double storage. Of course, except for types with nothrow copy constructors, this optimization is largely useless until the advent of a Boost.Move library. I do plan to submit one for inclusion in the Boost 1.32 release, but that presumably will not be for some months from now. _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost