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