Boost logo

Boost :

From: Aleksey Gurtovoy (alexy_at_[hidden])
Date: 2001-05-03 05:55:16


John Maddock wrote:
> This problem is not related to compressed_pair specifically,
> but will occur whenever an assignment to an empty base
> class occurs.

This is a known issue; the problem also appears if you let the compiler to
generate the derived class' assignment operator for you:

struct something_empty {};
struct luckless : something_empty
    {
    std::string text;
#if 0 // 1 here fixes the problem
    luckless& operator=(const luckless& other)
        {
        text = other.text;
        return *this;
        }
#endif
    };

int main()
    {
    luckless ll1;
    luckless ll2;
    ll1 = ll2; // crash here
    return 0;
    }

A subtle detail here is that you will see the problem only if the very first
member of your derived class has non-trivial assignment operator
("non-trivial" not in sense how the standard defines the term, but in some
rather specific way - the one that manages some resources, e.g. memory); for
example, the following definition of 'luckless' passes the above test just
fine (and assignment indeed does the right thing):

struct luckless : something_empty
    {
    int i;
    std::string text;
    };

and all 4 definitions below reproduce the problem with 100% probability (at
least on my system):

struct nontrivial
    {
    nontrivial() : ptr_(new int(5)) {}
    nontrivial(nontrivial const& other) : ptr_(new int(*other.ptr_)) {}
    nontrivial& operator=(nontrivial const& other)
        {
        nontrivial(other).swap(*this);
        return *this;
        }

    nontrivial& swap(nontrivial& other)
        {
        std::auto_ptr<int> ptr(ptr_);
        ptr_ = other.ptr_;
        other.ptr_ = ptr;
        return *this;
        }

    std::auto_ptr<int> ptr_;
    };

1)
struct luckless : something_empty
    {
    nontrivial m;
    };

2) typedef boost::compressed_pair<std::string, something_empty> luckless;
3) typedef boost::compressed_pair<nontrivial, something_empty> luckless;
4)
struct luckless : boost::equality_comparable<luckless>
    {
    nontrivial m;
    };

So things are indeed not very good. Of course, a workaround of the
compressed_pair's assignment problem (ll1 = ll2;) is to explicitly define
the copy assignment operator for it; but I don't see what we can do about
the original problem:

boost::compressed_pair<int, empty_class> cp;
cp.second() = some_empty_class_value;

and cases like 4-th one from the above code, except documenting the issues
so users could be aware of them. (well, as for 'compressed_pair', we could
make MSVC versions of the class 'first' or/and 'second' method(s) to return
a copy of the empty class instead of a reference to it , but as I see it
now, it creates even more problems). hmm.., probably I wasn't very helpful
:(...

Aleksey


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