Boost logo

Boost :

Subject: Re: [boost] [optional] Thoughts on disallowing assignment for wrapped references.
From: Mostafa (mostafa_working_away_at_[hidden])
Date: 2011-09-09 23:59:38


On Thu, 08 Sep 2011 13:01:46 -0700, Nevin Liber <nevin_at_[hidden]>
wrote:

> On 8 September 2011 08:10, Mostafa <mostafa_working_away_at_[hidden]>
> wrote:
>
>> In what respects haven't I covered it?
>
> Variables that are returned from functions are neither locals nor
> class members; you don't know what the caller wants to do with it.
>

I'm confused, what does this have to do with disallowing the assignment
operator for optional<T&>? Can you provide a concrete example?

>>> which again, is problematic for two reasons:
>>>
>>> (1) Types that are Copyable but not Assignable are surprising
>>
>> Not necessarily, think of Pimpl. If I have a Pimpl class heirarchy,
>> then
>> operator= becomes problematic for the base class, therefore I disallow
>> it in
>> all cases.
>
<snip>
>
>> What about using such a Pimpl in stl-like containers? Answer, use the
>> opaque handle of the Pimpl, and reconstruct the Pimpl from the opaque
>> handle
>> where necessary.
>
> A Pimpl with a shared_ptr works perfectly well in STL-like containers.

<snip>

Sorry, you missed my point. Let me provide a clarifying example:

class Shape
{
public:
   typedef ShapeImpl * OpaquePtr;
public:
   OpaquePtr getHandle(); //For use with stl-containers
   Shape(Shape & rhs);
   Shape(OpaquePtr handle);
private:
   //Disallowed, I don't want to deal with this error that can
   //only be caught at runtime:
   // Shape isCircle(...); Shape isPolygon(...); isCircle = isPolygon;
   virtual Shape & operator=(Shape const &);
protected:
   ShapeImpl * m_pImpl;
};

class Circle : public class Shape
{
public:
   typedef CircleImpl * OpaquePtr;
public:
   OpaquePtr getHandle(); //For use with stl-containers
   Circle(Circle & rhs);
   Circle(OpaquePtr handle);
private:
   //Disallowed.
   Circle & operator=(Circle const &);
};

>>> (2) It is rare to return a reference anyway, as something outside of
>>> the callee has to manage the lifetime of the underlying object
>>
>> Not that rare. Let's say I'm using raw-pointer/reference idiom to
>> convey
>> the semantics of optionalness, then returning a reference is certainly
>> an
>> option. I believe it's the callee that has to manage the said lifetime.
>
> How? The callee *can't* manage the lifetime of the underlying object,
> as all the local callee state is gone once the callee returns.
> Something else has to manage the underlying object so that it lasts as
> long as the caller needs it to exist (which may be longer than the
> caller itself exists).
>
> For instance, when vector::operator[] returns a reference, it assumes
> something else is keeping the vector around and not invalidating that
> reference for as long as that reference is needed. vector itself
> cannot manage that.
>
>
> Instead of making all these theoretical arguments, could you post some
> code where this would actually be useful?

class ShapeLib
{
public:
   void loadShapes();
   void dumpShapes();

   //All returned references and pointers valid between calls of load and
dump.

   Shape & regularShape();
   //If there's a blue moon returned object is valid, else not.
   Shape * onceEveryBlueMoonShape();
};

If the callee is a method then it manages the lifetime of the returned
objects via its owning object.

Mostafa


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