Boost logo

Boost :

From: Gennaro Prota (gennaro_prota_at_[hidden])
Date: 2003-04-03 07:15:27


Among the several changes I've planned to dynamic_bitset there's one
which affects semantics and that, therefore, I would like to discuss
with you a bit. Note however that the old semantics were never
specified in the docs, so this would break nothing but adventurous
usages of undocumented features on the users part.

Here's the issue: dynamic_bitset<>::reference currently stores a
pointer to its bitset object and an index. For that reason:

 - after a swap of bitsets, references come to refer to
   different elements: they get track of the address of the
   bitset they are referencing, and the bitset has changed
   its contents.

 - value changing operations on the dynamic_bitset *don't*
   invalidate references unless they make the corresponding
   index out of range (by shrinking the bitset). In other
   words: semantics are intrinsecally those of a *pair*
   (bitset address, index); as long as the index remains
   valid for the pointed to bitset everything is fine.
   But then one wonders: why not using an index directly?
   And even if, how could one use a reference considering
   that all its constructors are private?

Both points above are the exact contrary of what one would expect by
analogy with references, pointers, or iterators (The standard
guarantees that no standard library swap() function invalidates any
references, pointers, or iterators referring to *the elements* of the
containers being swapped. To state it metaphorically: the elements
change containers but references "follow" them).

Now, the first question is: ok to change semantics and document the
new behavior? (BTW, the new semantics can be implemented much more
efficiently, e.g. by storing a pointer to a block and a *precomputed*
mask)

Second question: any interest in having this new reference<Block> as a
separate class? (In this case dynamic_bitset::reference would really
become a typedef as stated in the docs, rather than a nested class
:-))

Now, for the gurus: this is the current interface of reference:

class reference
{
    friend class dynamic_bitset<Block, Allocator>;
    
    // the one and only non-copy ctor
    reference(dynamic_bitset& bs_, size_type bit_);

public:
    operator bool() const; // for x = b[i]
    bool operator~() const; // flips the bit
    reference& flip(); // for b[i].flip();

    reference& operator=(bool value); // for b[i] = x
    reference& operator|=(bool value); // for b[i] |= x
    reference& operator&=(bool value); // for b[i] &= x
    reference& operator^=(bool value); // for b[i] ^= x
    reference& operator-=(bool value); // for b[i] -= x

    reference& operator=(const reference& j); // for b[i] = b[j]
    reference& operator|=(const reference& j); // for b[i] |= b[j]
    reference& operator&=(const reference& j); // for b[i] &= b[j]
    reference& operator^=(const reference& j); // for b[i] ^= b[j]
    reference& operator-=(const reference& j); // for b[i] -= b[j]

};

Note the last two groups of 5 functions: they are identical except
that those of the first group take a bool and those of the second one
a const reference &. But since we have an operator bool() const
wouldn't the first group be enough?

In effect, declaring the copy-assignment operator has the pleasant
effect to force an explicit definition, showing that the shallow-copy
is intentional, but what about the other 4 functions? What's their
purpose? If we drop them, an expression like

   b[i] |= b[j]

changes from

   (b.operator[](i)).operator |= ( b.operator[](j) )

to

   (b.operator[](i)).operator |= ( b.operator[](j).operator bool() )

But I can't see any problem with that, even with self-assignment. Am I
missing something?

Genny.


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