Boost logo

Boost :

From: Eric Friedman (ebf_at_[hidden])
Date: 2003-10-28 17:08:49


E. Gladyshev wrote:
> --- Eric Friedman <ebf_at_[hidden]> wrote:
>
>
>>Are you referring to the following problem?
>>
>> T f();
>>
>> class my_type
>> {
>> T t_;
>> public:
>> my_type() { }
>> my_type(const my_type& other)
>> : t_( f() )
>> {
>> }
>> };
>>
>> variant< int, my_type > v;
>>
>> int main()
>> {
>> my_type m1;
>>
>> my_type m2 = m1; // ok
>> v = m1; // potentially undefined: does f visit v?
>> }
>
>
> Yes, this is what I meant.
>
>
>>If so, then yes, I've devised a solution for that problem, but it incurs
>>quite a costly run-time overhead (i.e., a backup copy of lhs content
>>required even when heap backup of lhs content is not).
>
>
> I thought that you had a "simple" solution
> that involves just an extra pointer?
> I have not given a lot of thought to this problem,
> but why is it costly? Do you mean that variant
> will have to allocate memory even if one of the
> types has non-throw copy-constructor?

Well, "simple" isn't the same as "cheap."

Here is my idea:

   variant assignment (lhs = rhs):
   * copy content of lhs (onto stack or heap, as appropriate)
   * set "temporary content" pointer in variant to copied content
   * destroy content in storage of lhs
   * copy content of rhs into storage of lhs (***)
   * unset "temporary content" pointer, and indicate new content type
   * destroy copied content (via delete or stack unwinding, as needed)

The idea here is that any visitation that might occur in the process of
copying rhs (on line ***) will be directed to the "temporary content"
pointer. (Thus the need for the additional pointer inside variant.)

The cost comes in the first line. While in the case that temporary heap
backup is used, this copy is already needed. But in the other cases,
where backup is unnecessary (because of nothrow copy guarantees or
because of a fallback type), this copy is not needed. [This is what I
meant by "a backup copy of lhs content required even when heap backup of
lhs content is not."]

Since such backup is typically not needed -- indeed, only in the
specific case that the variant being modified is accessed from the copy
constructor of a type in the variant -- I think you'll agree such
overhead is a bit excessive.

>>A better solution, I believe, is simply to document that accessing a
>>variant *during* assignment will result in undefined behavior. That way,
>>unless the user really needs to do something like the above, such (IMHO,
>>excessive) overhead will be avoided.
>
> I think that documenting the special case is almost as
> good as to say that variant doesn't really
> provide basic guarantees?

It always provides basic guarantees except in the following (peculiar)
situation:

   A variant's state is undefined if accessed *during* its assignment.

I think documenting this point is sufficient. If the user really needs
to do something like this, the variant can be allocated on the heap
(i.e., wrapped in a pimpl).

The other possibility is that the library provide a global_variant class
template for such situations. The most elegant solution is probably a
specialized assignment policy class. I'll keep this in mind when I
design the policy-based variant for 1.32.

Hopefully this addresses your concerns.

Eric


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk