|
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