[optional] Thoughts on disallowing assignment for wrapped references.

Boost.Optional seems to really have a lot of potential for what I want to do, but one thing about it has been nagging me, specifically it's assignment semantics for wrapped references being different from the semantics of bare C++ references (see http://tinyurl.com/4yna643). Has/have the author(s) of the library or anyone considered disallowing assignment for Boost.Optional types that wrap references, and if so, what would the consequences of this entail? I rather disallow assignment in this case, then have what's basically a wrapper class behave partially differently than the type it's supposed to emulate. Thanks in advance, Mostafa

On Tue, Aug 30, 2011 at 10:23 PM, Mostafa <mostafa_working_away@yahoo.com>wrote:
Boost.Optional seems to really have a lot of potential for what I want to do, but one thing about it has been nagging me, specifically it's assignment semantics for wrapped references being different from the semantics of bare C++ references (see http://tinyurl.com/4yna643).
So...you don't agree with the rationale?
Has/have the author(s) of the library or anyone considered disallowing assignment for Boost.Optional types that wrap references, and if so, what would the consequences of this entail? I rather disallow assignment in this case, then have what's basically a wrapper class behave partially differently than the type it's supposed to emulate.
Given that there's an entire section of the documentation devoted to the semantics of assignment, I'd infer the author(s) consider the various possibilities for assignment, including disallowing it. If you disallowed assignment for optional<T&>, then surely you would likewise need to disallow default constructability, and then how would an optional<T&> really be any different from a T& other than the syntactic clutter? Perhaps you can elaborate on why the current assignment semantics are troubling to you...? - Jeff

On Wed, 31 Aug 2011 01:07:36 -0700, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
On Tue, Aug 30, 2011 at 10:23 PM, Mostafa <mostafa_working_away@yahoo.com>wrote:
Boost.Optional seems to really have a lot of potential for what I want to do, but one thing about it has been nagging me, specifically it's assignment semantics for wrapped references being different from the semantics of bare C++ references (see http://tinyurl.com/4yna643).
So...you don't agree with the rationale?
I don't believe I wrote anything to imply that. I had a hard time following the rationale, so I've taken the author(s) at their word.
Has/have the author(s) of the library or anyone considered disallowing assignment for Boost.Optional types that wrap references, and if so, what would the consequences of this entail? I rather disallow assignment in this case, then have what's basically a wrapper class behave partially differently than the type it's supposed to emulate.
Given that there's an entire section of the documentation devoted to the semantics of assignment, I'd infer the author(s) consider the various possibilities for assignment, including disallowing it.
They may have considered disallowing it, but there's no mention of that consideration in the document, nor, assuming that they did consider such a possibility, why they rejected it.
If you disallowed assignment for optional<T&>, then surely you would likewise need to disallow default constructability, and then how would an optional<T&> really be any different from a T& other than the syntactic clutter?
Sorry, I don't follow you here. A defaulty constructed "optional<T&> pp;" would convey that someone has decided not to set the value of pp, exactly because the type of pp is *optional*<T&>. Hence, optional<T&> conveys more information than T&.
Perhaps you can elaborate on why the current assignment semantics are troubling to you...?
Let me reword and expand my concerns. I view boost::optional as a thin wrapper for it's underlying type, with the added sugar that it can convey whether its instance has or has not been set by the user. Hence, it behaves very much like a smart pointer. I expect pointer semantics to be a subset of smart pointer semantics, ie, whatever operations I can perform on pointers, I too can perform on smart pointers (where they are allowed) and get the same results. So for example, *p and p-> will give the same results regardless of whether p is a plain old pointer or a smart pointer. Where p is a smart pointer and "p + 5" is disallowed then I just say bummer, and do "p.get() + 5"; however, where "p + 5" was allowed and it behaved differently than "p.get() + 5", then I would be unpleasantly shocked, no matter how in-your-face it may have been blared in the documentation. In large software projects, heck, even in medium-sized teams, not everyone will read the *whole* documentation, they'll just glance enough at it to figure out how to solve their immediate need. With respect to the documentation for Boost.Optional, yes, there is a whole section on option<T&> assignment, but it in no way stands out from the rest of the documentation, and even within the said section, the only clue as to why it's there in the first place is a bold "rebind" which automatically catches my eye, but no red flags, no caution sign, nothing else. But my beef isn't with the documentation, as I said, even if it was blaring with a flashing red sign, I still would be hesitant because *not everyone reads the whole documentation*, for good or bad. Hence my desire to disallow inconsistent behaviour with regards to existing convention. More specifically, and to answer your question, what bugs me is that the semantics of the operations that I can perform on T& are not a subset of the semantics of the same operations that I can perform on optional<T&>, per the documentation: "Now, if you assign to an initialized optional<T&>, the effect is to rebind to the new object instead of assigning the referee. This is unlike bare C++ references." The rationale for such a rebinding of optional<T&> maybe very sound, and rebinding maybe the only choice to achieve consistent behaviour with respect to Boost.Optional, but it doesn't negate the fact that the semantics of the assignment operation that I perform on T& are not a subset of the semantics of the same operation that I perform on optional<T&>. And given the near universal rule that not all programmers read the whole documentation, there will inevitably come along some programmer who will assume that optional<T&> behaves at least like T& wherever the operations are defined on both (and rightly so in my opinion), and in the course of doing some maintenance on some large code base, the said programmer will introduce a bug that maybe easy to detect but will be hard to diagnose because of the need to reread the documentation. Hence my desire to explore the consequences of disallowing the assignment operation for optional<T&> and the reason for starting this thread. Hopefully this explains things better, Mostafa

On 31 August 2011 04:56, Mostafa <mostafa_working_away@yahoo.com> wrote:
Let me reword and expand my concerns. I view boost::optional as a thin wrapper for it's underlying type, with the added sugar that it can convey whether its instance has or has not been set by the user. Hence, it behaves very much like a smart pointer.
In "The models" section of the documentation, it quite clearly states (in a highlighted box no less) " optional<> is not, and does not model, a pointer." Your mental model is wrong. Are they reading *any* documentation? "Optional references" is mentioned more than once (in the table of contents) on the front page of the documentation. I don't know how you solve the problem of people not reading documentation, because the number of different mental models that people can make up that don't fit the implementation is unbounded. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On Wed, 31 Aug 2011 09:28:49 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 31 August 2011 04:56, Mostafa <mostafa_working_away@yahoo.com> wrote:
Let me reword and expand my concerns. I view boost::optional as a thin wrapper for it's underlying type, with the added sugar that it can convey whether its instance has or has not been set by the user. Hence, it behaves very much like a smart pointer.
In "The models" section of the documentation, it quite clearly states (in a highlighted box no less) " optional<> is not, and does not model, a pointer." Your mental model is wrong.
I'm quite aware of that. Unfortunately, the passage in which I was quoted was taken quite out of context. Frankly, I too could have been more careful in my wording. In the context of the my lengthy passage, what I was trying to convey is that just as smart pointer is a thin wrapper for its underlying type, so too optional is a thin wrapper for its underlying type. Hence, don't think I have the mental model wrong. (Given that my previous post was a little lengthy, maybe a rereading of my previous post will help clarify my position.)
Are they reading *any* documentation? "Optional references" is mentioned more than once (in the table of contents) on the front page of the documentation.
As I mentioned in my previous post, there is no indication that this section points out inconsistent behaviour with regards to bare C++ references. It has been my experience that programmers read documentation more thoroughly when they're developing, versus when they're maintaining, especially if it's other peoples code that they maybe enhancing/fixing.
I don't know how you solve the problem of people not reading documentation, because the number of different mental models that people can make up that don't fit the implementation is unbounded.
I agree with you, and that's why, IMHO, implementations should follow existing conventions as much as possible. And that's why I was exploring the disallowing of the assignment operator for optional<T&>, because in some use cases the behaviour of the assignment operation for optional<T&> is inconsistent with an existing convention, namely that of bare C++ references. Hopefully this clarifies what I was trying to say, Mostafa

On 8/31/2011 5:13 PM, Mostafa wrote:
On Wed, 31 Aug 2011 09:28:49 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 31 August 2011 04:56, Mostafa <mostafa_working_away@yahoo.com> wrote:
Let me reword and expand my concerns. I view boost::optional as a thin wrapper for it's underlying type, with the added sugar that it can convey whether its instance has or has not been set by the user. Hence, it behaves very much like a smart pointer.
In "The models" section of the documentation, it quite clearly states (in a highlighted box no less) " optional<> is not, and does not model, a pointer." Your mental model is wrong.
I'm quite aware of that. Unfortunately, the passage in which I was quoted was taken quite out of context. Frankly, I too could have been more careful in my wording. In the context of the my lengthy passage, what I was trying to convey is that just as smart pointer is a thin wrapper for its underlying type, so too optional is a thin wrapper for its underlying type. Hence, don't think I have the mental model wrong. (Given that my previous post was a little lengthy, maybe a rereading of my previous post will help clarify my position.)
Are they reading *any* documentation? "Optional references" is mentioned more than once (in the table of contents) on the front page of the documentation.
As I mentioned in my previous post, there is no indication that this section points out inconsistent behaviour with regards to bare C++ references.
It has been my experience that programmers read documentation more thoroughly when they're developing, versus when they're maintaining, especially if it's other peoples code that they maybe enhancing/fixing.
I don't know how you solve the problem of people not reading documentation, because the number of different mental models that people can make up that don't fit the implementation is unbounded.
I agree with you, and that's why, IMHO, implementations should follow existing conventions as much as possible. And that's why I was exploring the disallowing of the assignment operator for optional<T&>, because in some use cases the behaviour of the assignment operation for optional<T&> is inconsistent with an existing convention, namely that of bare C++ references.
Hopefully this clarifies what I was trying to say,
If you want to change the assignment operator for optional when the type is a reference, just derive a class template from it, change what you want, and use that template to create object types rather than optional. While you are of course free to have your own opinion on the matter, and argue for it, citing that programmers will not be used to the functionality, and that they are normally not going to read the documentation to understand it and its reason, is not a strong argument.

On Wed, 31 Aug 2011 16:09:24 -0700, Edward Diener <eldiener@tropicsoft.com> wrote: <snip>
If you want to change the assignment operator for optional when the type is a reference, just derive a class template from it, change what you want, and use that template to create object types rather than optional.
While you are of course free to have your own opinion on the matter, and argue for it, citing that programmers will not be used to the functionality, and that they are normally not going to read the documentation to understand it and its reason, is not a strong argument.
Just to clarify, if it was not clear enough in my original message, the *intent of this thread* is not to change the existing behaviour of optional, or argue for a change, rather, it is to understand the implications of a change, since I assume there are people on this forum that are more knowledgeable than me with respect to the usage of Boost.Optional. As for your latter remarks, I would have to strongly disagree with that, and I'll just leave it at that, since my motivation for exploring the implications of the said change to optional is tangential to the intent of this thread. Mostafa

On 31 August 2011 16:13, Mostafa <mostafa_working_away@yahoo.com> wrote:
I agree with you, and that's why, IMHO, implementations should follow existing conventions as much as possible. And that's why I was exploring the disallowing of the assignment operator for optional<T&>, because in some use cases the behaviour of the assignment operation for optional<T&> is inconsistent with an existing convention, namely that of bare C++ references.
I still contend that your mental model is wrong with respect to C++ references (but understandably so, as I'll get to). Let's start with local objects. Take: { Foo f; // f exists //... } // f will shortly get destroyed The lifetime of f is from when it is done being constructed until a little after its enclosing scope is destroyed (at which time objects are destroyed in reverse order of creation). Within that stack frame, f exists exactly 1 time; no more, no less. Now look at member variables: struct Bar { Foo f; }; The lifetime of Bar::f is from when it Bar::f is constructed until shortly after the Bar object is destroyed. Within any instance of Bar, f exists exactly 1 time; no more, no less. I'll call the above two examples the normal lifetime window. If you pair up new/delete, I can define a similar window for heap based objects as well, but by now you get the idea. Moving on to boost::optional for objects: { boost::optional<Foo> maybef; // ... } Within the normal lifetime window, the object maybef holds can exist 0 or more times. Those are the semantics boost::optional gives us. Moving on to C++ references: { Foo& reff /* what goes here depends on if it is a local or class member... */; // ... } Within the normal lifetime window, the reff is bound to an object exactly 1 time; no more, no less. Given that, within the normal lifetime window, what would you expect to happen for an optional reference: { boost::optional<Foo&> maybereff; // ... } I see the same thing as all of the other cases, the "exactly 1 time" is relaxed into "0 or more times". So why do people get this wrong? We don't tend to be very precise when we describe what happens in English. We say "one cannot rebind a reference", which brings to mind "at most 1 time" semantics, and *we mentally separate that* from "bound at construction time" ("at least 1 time" semantics), even though the two are inextricably linked ("exactly 1 time" semantics). -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On Wed, 31 Aug 2011 22:13:30 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 31 August 2011 16:13, Mostafa <mostafa_working_away@yahoo.com> wrote:
I agree with you, and that's why, IMHO, implementations should follow existing conventions as much as possible. And that's why I was exploring the disallowing of the assignment operator for optional<T&>, because in some use cases the behaviour of the assignment operation for optional<T&> is inconsistent with an existing convention, namely that of bare C++ references.
I still contend that your mental model is wrong with respect to C++ references (but understandably so, as I'll get to).
Let's start with local objects. Take:
{ Foo f; // f exists //... } // f will shortly get destroyed
The lifetime of f is from when it is done being constructed until a little after its enclosing scope is destroyed (at which time objects are destroyed in reverse order of creation). Within that stack frame, f exists exactly 1 time; no more, no less.
Now look at member variables:
struct Bar { Foo f; };
The lifetime of Bar::f is from when it Bar::f is constructed until shortly after the Bar object is destroyed. Within any instance of Bar, f exists exactly 1 time; no more, no less.
I'll call the above two examples the normal lifetime window. If you pair up new/delete, I can define a similar window for heap based objects as well, but by now you get the idea.
Moving on to boost::optional for objects:
{ boost::optional<Foo> maybef; // ... }
Within the normal lifetime window, the object maybef holds can exist 0 or more times. Those are the semantics boost::optional gives us.
Moving on to C++ references:
{ Foo& reff /* what goes here depends on if it is a local or class member... */; // ... }
Within the normal lifetime window, the reff is bound to an object exactly 1 time; no more, no less.
Given that, within the normal lifetime window, what would you expect to happen for an optional reference:
{ boost::optional<Foo&> maybereff; // ... }
I see the same thing as all of the other cases, the "exactly 1 time" is relaxed into "0 or more times".
Ahh, I disagree with that. IMO, if maybereff was not bound at construction time, then it should always remain "uninitialized". That's another reason why I wanted to explore the implications of disallowing the assignment operator for optional<T&>. Now Fernando has made an interesting case for why disallowing the assignment operator for optional<T&> and allowing it for all other types is bad. I will be responding to Fernando with some more question(s). I would also be interested in knowing your answers to those question(s). Mostafa

On 1 September 2011 04:28, Mostafa <mostafa_working_away@yahoo.com> wrote:
I see the same thing as all of the other cases, the "exactly 1 time" is relaxed into "0 or more times".
Ahh, I disagree with that.
That may be, but you haven't shown a flaw in my reasoning. I contend that in the normal lifetime window, both objects and references exist "exactly 1 time". Where is that reasoning inconsistent?
IMO, if maybereff was not bound at construction time, then it should always remain "uninitialized".
You are arguing for "at most once where lifetime begins at the point of construction of the optional<U&>"? Why not argue the same for objects? I don't see the difference, other than you want to introduce a difference.
Now Fernando has made an interesting case for why disallowing the assignment operator for optional<T&> and allowing it for all other types is bad.
He is arguing for consistency. So am I. Construction/assignment of an optional<T> is for affecting the lifetime of the underlying T object it holds. Assignment doesn't affect lifetime of C++ objects or references, so if one is going to use that syntax for optional<T>, one has to define what the semantics should be in both the case where the underlying object or reference does not yet exist and in the case where the underlying object or reference already exists. Assignment to an optional<T> almost models destroying the underlying T object (if it already exists) followed by creating a new underlying object. In the case of objects, it performs an optimization by using assignment instead of destroy/construct in the case where the object already exists (which is why I said "almost models" in the previous statement). That optimization does not exist for references. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On Thu, 01 Sep 2011 07:19:05 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 1 September 2011 04:28, Mostafa <mostafa_working_away@yahoo.com> wrote:
I see the same thing as all of the other cases, the "exactly 1 time" is relaxed into "0 or more times".
Ahh, I disagree with that.
That may be, but you haven't shown a flaw in my reasoning. I contend that in the normal lifetime window, both objects and references exist "exactly 1 time". Where is that reasoning inconsistent?
I'm not disagreeing with your above comment, rather I'm disagreeing with how: { boost::optional<Foo&> maybereff; // ... } should behave.
IMO, if maybereff was not bound at construction time, then it should always remain "uninitialized".
You are arguing for "at most once where lifetime begins at the point of construction of the optional<U&>"? Why not argue the same for objects? I don't see the difference, other than you want to introduce a difference.
Because for optional<T&>, and only for optional<T&>, the semantics of the assignment operator differs from the semantics of the assignment operator of its underlying type, namely T&. (Hence the reason for me wanting to explore disallowing the assignment operator for optional<T&>.)
Now Fernando has made an interesting case for why disallowing the assignment operator for optional<T&> and allowing it for all other types is bad.
He is arguing for consistency. So am I.
Aren't we all? But there are differing notions of consistency floating around. I'm motivated by consistency for the assignment operator of optional<T&>, Fernando's motivated by consistency of programming w.r.t. to template programming and use of optional in general. And as Fernando pointed out, it may not be possible to satisfy all these notions simultaneously. My thought was, and continues to be that if all these notions of consistency can't be satisfied simultaneously, then instead of satisfying some fully and others partially, why not just disallow those operations that are partially consistent. Or more specifically, what are the implications of just disallowing those operations that are partially consistent? Mostafa

On Sat, Sep 3, 2011 at 7:55 PM, Mostafa <mostafa_working_away@yahoo.com>wrote: [...]
Aren't we all? But there are differing notions of consistency floating around. I'm motivated by consistency for the assignment operator of optional<T&>, Fernando's motivated by consistency of programming w.r.t. to template programming and use of optional in general. And as Fernando pointed out, it may not be possible to satisfy all these notions simultaneously. My thought was, and continues to be that if all these notions of consistency can't be satisfied simultaneously, then instead of satisfying some fully and others partially, why not just disallow those operations that are partially consistent. Or more specifically, what are the implications of just disallowing those operations that are partially consistent?
One implication would be a breaking of existing code. Another implication would be greater difficulty in using optional<T> in a generic context when T could be a reference type. - Jeff

On Sat, 03 Sep 2011 20:34:41 -0700, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
On Sat, Sep 3, 2011 at 7:55 PM, Mostafa <mostafa_working_away@yahoo.com>wrote: [...]
Aren't we all? But there are differing notions of consistency floating around. I'm motivated by consistency for the assignment operator of optional<T&>, Fernando's motivated by consistency of programming w.r.t. to template programming and use of optional in general. And as Fernando pointed out, it may not be possible to satisfy all these notions simultaneously. My thought was, and continues to be that if all these notions of consistency can't be satisfied simultaneously, then instead of satisfying some fully and others partially, why not just disallow those operations that are partially consistent. Or more specifically, what are the implications of just disallowing those operations that are partially consistent?
One implication would be a breaking of existing code. Another implication would be greater difficulty in using optional<T> in a generic context when T could be a reference type.
That seems to be the consensus so far. Assuming that I'm creating optional2, so we don't have to worry about existing code, and that it's equivalent to optional in all respects except that the assignment operator has now been disallowed across the board, and we've only retained a no-argument reset method. What are the implications of that? The only consequence I see is that optional2 may not be used with stl-like containers. And how prevalent is the use of the existing optional with stl-like containers anyway? What's the motivation for this line of inquiry? Partial inconsistency w.r.t. to the assignment operator is eliminated and the use of optional2 in a generic context is not comprised. Mostafa

On 4 September 2011 00:14, Mostafa <mostafa_working_away@yahoo.com> wrote:
That seems to be the consensus so far. Assuming that I'm creating optional2, so we don't have to worry about existing code, and that it's equivalent to optional in all respects except that the assignment operator has now been disallowed across the board, and we've only retained a no-argument reset method. What are the implications of that?
The same problem that references and const variables have; namely, any class that has them as members is non-Assignable (both non-CopyAssignable and non-MoveAssignable). It's the reason I never have const member variables, and either use pointers, boost::optional or boost::reference_wrapper instead of C++ references as members. Not being useful as member variables is a severe limitation. And while I do occasionally have classes that are not Copyable, I can't think of any of those that I don't want to make Moveable in the near future. It isn't that vector<optional2<T>> doesn't work; it's that vector<anytypethathasoptional2asamember> doesn't work. This is a non-starter for many people. That leaves using your class for local variables or parameters, and I don't see much usefulness there either, as a pointer is a far easier construct to use for such a localized effect. Classes that are CopyConstructible or MoveConstructible but not Assignable are very rare and unexpected. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On Sun, 04 Sep 2011 00:56:50 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 4 September 2011 00:14, Mostafa <mostafa_working_away@yahoo.com> wrote:
That seems to be the consensus so far. Assuming that I'm creating optional2, so we don't have to worry about existing code, and that it's equivalent to optional in all respects except that the assignment operator has now been disallowed across the board, and we've only retained a no-argument reset method. What are the implications of that?
The same problem that references and const variables have; namely, any class that has them as members is non-Assignable (both non-CopyAssignable and non-MoveAssignable).
Ok, that's a valid concern. I forgot to generalize the implication to the composing class.
That leaves using your class for local variables or parameters, and I don't see much usefulness there either, as a pointer is a far easier construct to use for such a localized effect.
I'm going to go off-topic here, but it's going to explain my reasons for considering the use of optional. The problem with using pointers/references to represent the "is optional"/"is required" idiom is two-fold: 1) Raw Pointers Raw pointers don't convey the semantics of their life-time policy, hence one has to rely on some convention, which can easily change from project to project even within the same company. Hence the motivation for using smart pointers where feasible. 2) Smart Pointers Smart pointers are nice in that they convey the semantics of their life-time, but their have been many discussions on this forum which have advised against passing or returning a dereferenced smart pointer by reference. If we were to follow the latter advice, and I am inclined to do so, then smart pointers become a noop for the aforementioned idiom. And this creates a new problem, in that now smart pointers don't convey the semantics of their "optionalness". Which brings me to Boost.Optional. Boost.Optional is nice because it conveys its intent explicitly, and I can use it directly with smart pointers. Mostafa

On 6 September 2011 04:52, Mostafa <mostafa_working_away@yahoo.com> wrote:
1) Raw Pointers Raw pointers don't convey the semantics of their life-time policy,
It is stronger than that. Raw pointers don't have a lifetime policy.
2) Smart Pointers Smart pointers are nice in that they convey the semantics of their life-time,
They don't just convey them; they enforce them.
but their have been many discussions on this forum which have advised against passing or returning a dereferenced smart pointer by reference.
That sounds like bad advice (and I've said so on previous occasions), at least as far as passing in parameters to a function. My advice is simple: if the callee isn't mucking with lifetime/ownership, do not pass in a smart pointer; rather, pass in a raw C++ reference or pointer. Returning something, by definition, mucks with lifetime (since you want to extend it past the lifetime of the callee, and you have no idea how long the caller wants to extend it past that), so you want to return a smart pointer.
If we were to follow the latter advice, and I am inclined to do so,
Um, okay I guess. But by following that advice, you end up making these issues far more complicated than they need to be.
then smart pointers become a noop for the aforementioned idiom. And this creates a new problem, in that now smart pointers don't convey the semantics of their "optionalness".
Like I said, complicated... Just for fun: in your world, what would you expect the semantics of optional2< optional2<T&> > to be? :-) -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On 3 September 2011 21:55, Mostafa <mostafa_working_away@yahoo.com> wrote:
Because for optional<T&>, and only for optional<T&>, the semantics of the assignment operator differs from the semantics of the assignment operator of its underlying type, namely T&. (Hence the reason for me wanting to explore disallowing the assignment operator for optional<T&>.)
boost::optional is very consistent with respect to assignment. Given boost::optional<T> ot; 'ot = u' deals with the existence of the underlying object, while '*ot = v' deals with the value of the underlying object. Now, in C++, there is no such thing as existence without value, so you cannot escape dealing with the underlying value in the expression 'ot = u'. That holds for non-reference types as well as for references. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On Sun, 04 Sep 2011 00:33:44 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 3 September 2011 21:55, Mostafa <mostafa_working_away@yahoo.com> wrote:
Because for optional<T&>, and only for optional<T&>, the semantics of the assignment operator differs from the semantics of the assignment operator of its underlying type, namely T&. (Hence the reason for me wanting to explore disallowing the assignment operator for optional<T&>.)
boost::optional is very consistent with respect to assignment. Given
boost::optional<T> ot;
'ot = u' deals with the existence of the underlying object, while '*ot = v' deals with the value of the underlying object. Now, in C++, there is no such thing as existence without value, so you cannot escape dealing with the underlying value in the expression 'ot = u'. That holds for non-reference types as well as for references.
The Boost.Optional documentation begs to differ, direct quote: "Rebinding semantics for the assignment of initialized optional references has been chosen to provide consistency among initialization states even at the expense of lack of consistency with the semantics of bare C++ references." See: http://tinyurl.com/4yna643 Mostafa

On 6 September 2011 04:57, Mostafa <mostafa_working_away@yahoo.com> wrote:
'ot = u' deals with the existence of the underlying object, while '*ot = v' deals with the value of the underlying object. Now, in C++, there is no such thing as existence without value, so you cannot escape dealing with the underlying value in the expression 'ot = u'. That holds for non-reference types as well as for references.
The Boost.Optional documentation begs to differ, direct quote:
"Rebinding semantics for the assignment of initialized optional references has been chosen to provide consistency among initialization states even at the expense of lack of consistency with the semantics of bare C++ references."
I was looking at it that the "value" of a reference is the object it refers to, and not the value of that object itself. Sorry for not being clear. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

Mostafa wrote:
On Wed, 31 Aug 2011 22:13:30 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 31 August 2011 16:13, Mostafa <mostafa_working_away@yahoo.com> wrote:
Ahh, I disagree with that. IMO, if maybereff was not bound at construction time, then it should always remain "uninitialized".
If the only way to make a non-empty optional<T&> is to construct it from a reference, then it couldn't be used, for example, as a function return type. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Fri, 02 Sep 2011 11:10:01 -0700, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Mostafa wrote:
On Wed, 31 Aug 2011 22:13:30 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 31 August 2011 16:13, Mostafa <mostafa_working_away@yahoo.com> wrote:
Ahh, I disagree with that. IMO, if maybereff was not bound at construction time, then it should always remain "uninitialized".
If the only way to make a non-empty optional<T&> is to construct it from a reference, then it couldn't be used, for example, as a function return type.
A non-empty maybereff can be bound at construction time via construction from a T& or from construction from a non-empty optional<T&>. The latter would allow it to be used as a function return type. Mostafa

Mostafa wrote:
On Fri, 02 Sep 2011 11:10:01 -0700, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Mostafa wrote:
Ahh, I disagree with that. IMO, if maybereff was not bound at construction time, then it should always remain "uninitialized".
If the only way to make a non-empty optional<T&> is to construct it from a reference, then it couldn't be used, for example, as a function return type.
A non-empty maybereff can be bound at construction time via construction from a T& or from construction from a non-empty optional<T&>.> The latter would allow it to be used as a function return type.
Right. When I wrote the above, however, I was thinking of the more generalized usage that is possible with optional, though I didn't say so: optional<T> f() { optional<T> result; // do stuff return result; } Since copy assignment isn't permitted in your idea, that wouldn't work. That means even less generality, so a new type would definitely be in order if shown to be sufficiently useful. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Tue, 06 Sep 2011 07:31:23 -0700, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Mostafa wrote:
On Fri, 02 Sep 2011 11:10:01 -0700, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Mostafa wrote:
Ahh, I disagree with that. IMO, if maybereff was not bound at construction time, then it should always remain "uninitialized".
If the only way to make a non-empty optional<T&> is to construct it from a reference, then it couldn't be used, for example, as a function return type.
A non-empty maybereff can be bound at construction time via construction from a T& or from construction from a non-empty optional<T&>.> The latter would allow it to be used as a function return type.
Right. When I wrote the above, however, I was thinking of the more generalized usage that is possible with optional, though I didn't say so:
optional<T> f() { optional<T> result; // do stuff return result; }
Since copy assignment isn't permitted in your idea, that wouldn't work. That means even less generality, so a new type would definitely be in order if shown to be sufficiently useful.
I'm presuming that in your "do stuff" section there is going to be something along the lines of "result = ...". If so, then: In this thread-path I was exploring disallowing assignment for optional<T&> and only optional<T&>. Hence the above example should work for all types T that are not references, but fail for types T that are references. However, in the latter case, there is no loss of generality since the above would still be a no-no where T a reference type and assignment allowed for optional<T&>. Or did I miss something in your response? There is some loss of generality w.r.t. to the existing behaviour of optional when optional<T&> is a member variable and one would want to return it from a method, since optional<T&>'s initialization is then restricted to its enclosing objects construction. However, that is how one would expect the underlying T& type to behave from the outset. Mostafa

Hi Mostafa,
optional<T> f() { optional<T> result; // do stuff return result; }
However, in the latter case, there is no loss of generality since the above would still be a no-no where T a reference type ... Or did I miss something in your response?
You totally missed his point. Please allow me to advise you on how to avoid that: do not to pick any spot to make a counter-argument. If you do, you'll miss the point, and you risk loosing our interest in constructively arguing with you. In fact, in this particular thread, it is you the one presenting an idea for us to discuss, so it is really important that you handle the discussion appropriately. In Robert's example, it is easy to see--or should be easy to see--that the fact that the optional<T> is being returned is secondary, perhaps even irrelevant, to the point he is making His point is about the shortcoming of lacking assignment, not about object lifetimes (and BTW, is the same point Nevin and I made earlier). Perhaps a less distracting example would be: optional<T> o ; switch ( cond ) { case a : o = foo1(); break ; case b : o = foo2(); break ; case c : o = foo3(); break ; case e : o = foo4(); break ; case e : o = foo5(); break ; default : // o remains uninitialized } Notice that is quite possible to encounter the above example in real-life, as it is common that a variable remains uninitialized in the default case of a switch (or, for that matter, in any branch of a complex conditional). In fact, I even wonder if that example couldn't be considered as the canonical use case for optional<>'s assignment operation. Best -- Fernando Cacciola SciSoft Consulting, Founder http://www.scisoft-consulting.com

On Tue, 06 Sep 2011 19:50:38 -0700, Fernando Cacciola <fernando.cacciola@gmail.com> wrote:
Hi Mostafa,
optional<T> f() { optional<T> result; // do stuff return result; }
However, in the latter case, there is no loss of generality since the above would still be a no-no where T a reference type ... Or did I miss something in your response?
You totally missed his point. Please allow me to advise you on how to avoid that: do not to pick any spot to make a counter-argument. If you do, you'll miss the point, and you risk loosing our interest in constructively arguing with you.
In fact, in this particular thread, it is you the one presenting an idea for us to discuss, so it is really important that you handle the discussion appropriately.
I think what you are pointing out are the dangers of straw man argumentation, and I agree with you. However, not all cases of straw man discussions are intentional. Sometimes, as in the above case, one party in the discussion may truly misunderstand the other's message, be it the communicatee's fault, the communicator's fault, or some combination of the two. I try my best to understand the other party's intent (and I assume so do others). The only remedy I see to such confusion is further communication.
In Robert's example, it is easy to see--or should be easy to see--that the fact that the optional<T> is being returned is secondary, perhaps even irrelevant, to the point he is making His point is about the shortcoming of lacking assignment, not about object lifetimes (and BTW, is the same point Nevin and I made earlier).
I still contend my previous assertion is true for all forms of Robert's example, and I will try to prove it. To recap, I claim: 1) Were the assignment operator disallowed for optional<T&> and only optional<T&>, there would no loss of programmatic generality when optional<T&> is used as a local variable. 2) Were the assignment operator disallowed for optional<T&> and only optional<T&>, there would be a loss of programmatic generality when optional<T&> is used as a member variable. I think 2) is easy to see, so I'll go about proving 1). Proof (or more correctly an outline of a proof): Let D1 = { x | x is a sequence of C++ declarations in some function foo, there exists o, o is a local instance of optional<T&>, o appears as an lvalue in some assignment expression in some declaration in x } Let D2 = { y | y is a sequence of C++ declarations in some function qux, there exists alt_opt, alt_opt<T> is equivalent to optional<T> in all respects except when T is reference type, when T is a reference type the assignment operators of alt_opt will be disallowed } Let F be a map, in the mathematical sense, from D1 into D2 defined for all x in D1 by: 1) For the initial, contiguous, top-most scopeS in x where a local instance o of optional<T&> appears as an lvalue in an assignment expression in some non-conditonal declaration: a) replace the last occurrence of said assignment expression in the enclosing scope with a 1-arg alt_opt<T&> constructor in F(x). b) all previous occurrences of said assignment expressions in the said enclosing scope for F(x) can be refactored such that the lvalue term is of type T&. 2) If in x there are any uses of o outside and after the said initial, contiguous, top-most scopeS indicated in 1), then: a) in F(x) refactor those use cases into another function, say bar2, call bar2 at the end of the enclosing scope for all occurrences of 1-arg alt_opt<T&> constructor declarations, b) apply F(x) recursively to bar2 The case of where a local instance o of optional<T&> appears as an lvalue in an assignment expression in some conditional declaration in x should now be trivial to handle, so I'll leave it out because it just adds verbosity to the proof. I will now demonstrate the application of F to an expanded version of the example that you gave in the immediately previous post. Attached are two files, D1.txt and D2.txt. The function in D2.txt is the result of applying F to the function in D1.txt, annotations are provided in D2.txt pointing out how F was applied. In fact, from the above proof, it should be easy to see that assertions 1) and 2) hold for any type T, not just optional<T&>. ------------------ The latter explanation may seem overly pedantic, however, since the assertion I made is directly relevant to the intent of this thread (exploring the consequences of disallowing the assignment operators for optional<T&>), and since you disagreed with my assertion, I saw it necessary to provide a justification for my assertion. ------------------ Thanks for clarifying the example, now I think I have enough information to understand what the relevant implications of disallowing assignment operators for optional<T&> would be, namely: 1) they may make generic programming harder because the generic programmer may have to handle the case of optional<T> and optional<T&> separately. 2) There would be some loss of generality w.r.t. to the existing behaviour of optional<T&> if optional<T&> were used as a member variable. 3) Even though there is no loss of generality w.r.t. to the existing behaviour of optional<T&> if optional<T&> were a local variable, the programming paradigm it forces on users may not be convenient. Mostafa

On 7 September 2011 23:15, Mostafa <mostafa_working_away@yahoo.com> wrote:
1) Were the assignment operator disallowed for optional<T&> and only optional<T&>, there would no loss of programmatic generality when optional<T&> is used as a local variable.
I just don't see a use case for when it is a local variable, as it would have the same functionality as a const pointer (with slightly different syntax). And it would be rare indeed for me to even use const in this case, as protecting oneself against localized coding dicdecisions is futile, since all it takes is one edit to change it with no repercussions outside of the function itself. The case we've mentioned that you haven't covered is returning it from a function, which again, is problematic for two reasons: (1) Types that are Copyable but not Assignable are surprising (2) It is rare to return a reference anyway, as something outside of the callee has to manage the lifetime of the underlying object -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On Wed, 07 Sep 2011 23:44:42 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 7 September 2011 23:15, Mostafa <mostafa_working_away@yahoo.com> wrote:
1) Were the assignment operator disallowed for optional<T&> and only optional<T&>, there would no loss of programmatic generality when optional<T&> is used as a local variable.
I just don't see a use case for when it is a local variable, ... <snip>
In trying to answer "what are the implications of disallowing the assignment operator for optional<T&>" I had to consider the all possible use cases of optional<T&>, and they are 1) where optional<T&> is used as a local variable, and 2) where optional<T&> is used as a member variable. Well, there are namespace variables too, but it's obvious the implications are the same as 2).
The case we've mentioned that you haven't covered is returning it from a function,
In what respects haven't I covered it?
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. 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.
(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. Mostafa

On 8 September 2011 08:10, Mostafa <mostafa_working_away@yahoo.com> 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.
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.
If you have an Impl class hierarchy, presumably you have to allocate it on the heap, so why isn't the Pimpl just storing it in a shared_ptr? That way, the compiler generated copy/move constructors and assignment operators just work.
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. You seem to revel in adding lots of complexity into what should be simple cases. The few cases I've seen of Copyable but not Assignable classes (including Pimpl) are those where the object cannot participate in the ownership of some resource (usually for performance reasons). In my world, a raw pointer/reference means the underlying object does not participate in lifetime/ownership, so it captures that idea perfectly. If I want to disable assignment, all I have to do is make it a const pointer. I just don't see what optional2 buys me. For a Pimpl, I don't even see what having an optional Impl buys me if I can't ever set it.
(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? -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On Thu, 08 Sep 2011 13:01:46 -0700, Nevin Liber <nevin@eviloverlord.com> wrote:
On 8 September 2011 08:10, Mostafa <mostafa_working_away@yahoo.com> 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

Hi Mostafa, Let me start by warning that this discussion is likely to wind up in an endless argument, and I might not have the time to follow up. In any case, if you google for it you should be able to find the really long discussions that led to the current choice. From the 3 possible choices, each one favors certain aspects at a certain expense, so no choice is a sensible winner, yet one has to be made, and so I did. A lot of people is concerned with the current semantics. And a lot of people was concerned with the old semantics (it used to assign the referee). In fact, the main reason (if not the only one) why Boost.Optional did not make it into Cpp0x is precisely the concerns about choice of assignment semantics for references. The problem with disabling assignment all toghether is basically the same as assigning the referee instead of the wrapper: it breaks consistency. In a highly generic context, which is so typical in modern C++, the T in optional<T> might very well be itself a reference type, so any special behavior might result in an practical shotcomming, even possibly a show stopper: it could simply rule out optional<> as an element of a generic library. To be a little more precise: A library that uses optional<T> such that the choice of T is entirely external to the library *requires* that optional<> is completely consistent regardless of T. If it where to behave specially in the case T is a reference type (or for that matter, a non-POD type, a compound type, or whatever) then the library itself would have to explicitely handle all such differences. Recall that in a generic context, the typename T could very well be a reference type even without the &, so any time you consider what optional<T&> should do, substitute that for optional<U> and think again. The current choice is to favor consistency for the sake of generic libraries, where the library itself would not discriminate whether T happens to be a reference type or not. Keep in mind that *Assignment to optional<T> is simply not the same as assignment to a T*. It cannot possibly be for the simple fact that you cannot assign to nothing. Hence, optional<> itself must define *its own* assignment semantics, so the argument that it should follow the assignment semantics of the underlying T is not that strong because it clearly cannot do that on all cases (when the lvalue is empty). It is a sensible argument of course, but it must be weighted against the equally sensible counter argument that it breaks consistency. In my balance, consistency wins because it has a direct practical importance: without it, certain uses of optional<> would just not be possible. Best P.S.: Please do not just counter argument my counter arguments without having first googled, read and processed this very same discussion in the past. -- Fernando Cacciola SciSoft Consulting, Founder http://www.scisoft-consulting.com

On Wed, 31 Aug 2011 09:41:58 -0700, Fernando Cacciola <fernando.cacciola@gmail.com> wrote:
Hi Mostafa,
Let me start by warning that this discussion is likely to wind up in an endless argument, and I might not have the time to follow up. In any case, if you google for it you should be able to find the really long discussions that led to the current choice.
Just to clarify, I'm not arguing for or from a certain position, rather, I'm looking for enlightenment as to what the consequences of disallowing the assignment operator for optional<T&> are. I would be greatly disappointed if you did not follow up to any of my direct queries to you, even if it is just a pointer to look elsewhere.
From the 3 possible choices, each one favors certain aspects at a certain expense, so no choice is a sensible winner, yet one has to be made, and so I did.
A lot of people is concerned with the current semantics. And a lot of people was concerned with the old semantics (it used to assign the referee). In fact, the main reason (if not the only one) why Boost.Optional did not make it into Cpp0x is precisely the concerns about choice of assignment semantics for references.
The problem with disabling assignment all toghether is basically the same as assigning the referee instead of the wrapper: it breaks consistency.
In a highly generic context, which is so typical in modern C++, the T in optional<T> might very well be itself a reference type, so any special behavior might result in an practical shotcomming, even possibly a show stopper: it could simply rule out optional<> as an element of a generic library.
To be a little more precise:
A library that uses optional<T> such that the choice of T is entirely external to the library *requires* that optional<> is completely consistent regardless of T. If it where to behave specially in the case T is a reference type (or for that matter, a non-POD type, a compound type, or whatever) then the library itself would have to explicitely handle all such differences. Recall that in a generic context, the typename T could very well be a reference type even without the &, so any time you consider what optional<T&> should do, substitute that for optional<U> and think again.
The current choice is to favor consistency for the sake of generic libraries, where the library itself would not discriminate whether T happens to be a reference type or not.
Ok, I think I understand your argument. To clarify, by consistency here you mean consistency of programming in a generic environment, so that, as you say, a generic library would not have to discriminate between T and T&. Disclaimer1: What I will say below may have already been discussed, if so, please feel free to tell me to google it. Disclaimer2: I have very little experience with writing generic libraries in the C++ sense. Disclaimer3: Besides the documentation, I have no experience in actually using optional. What are the consequences of doing away with the assignment operator totally, and just having a no-parameter reset method that resets optional<...> state to uninitialized? In my naive point-of-view, the majority of Boost.Optional use case would involve member-variable types, or interface level types, ie, parameter types and return types. Racking my brain, I can't think of a draw back with respect to this use case if the above thought experiment were carried out.
Keep in mind that
*Assignment to optional<T> is simply not the same as assignment to a T*.
It cannot possibly be for the simple fact that you cannot assign to nothing. Hence, optional<> itself must define *its own* assignment semantics, so the argument that it should follow the assignment semantics of the underlying T is not that strong because it clearly cannot do that on all cases (when the lvalue is empty). It is a sensible argument of course, but it must be weighted against the equally sensible counter argument that it breaks consistency.
To clarify, in explaining my motivation for the intent of this thread I was saying that a casual programmer would expect that the assignment semantics of T would be a subset of the assignment semantics of optional<T>, not the other way around. In general, for any wrapper type class "wrap" and wrapped type T, I think that a casual programmer would expect the semantics of the "natural operations" of T to be a subset of the semantics of the those same operations wherever defined on wrap. The reasoning being that the casual user of wrap who is accustomed to the conventions of T will not be unduly surprised when he/she applies those same conventions to wrap. And in this sense, there is no inconsistency between T and wrap, since the "natural operations" of T just seem to work with wrap, whenever wrap truly represents its underlying type. But, this is tangential to the intent of this thread.
P.S.: Please do not just counter argument my counter arguments without having first googled, read and processed this very same discussion in the past.
Fair, I understand not wanting to sound like a broken tape recorder. IMHO, this indicates a need to add a general "Design Rationale" section to the documentation, maybe incorporating summaries of the discussions that you allude to. Thanks for your enlightening response, Mostafa

Hi Mostafa,
On Wed, 31 Aug 2011 09:41:58 -0700, Fernando Cacciola <fernando.cacciola@gmail.com> wrote:
Hi Mostafa,
Let me start by warning that this discussion is likely to wind up in an endless argument, and I might not have the time to follow up. In any case, if you google for it you should be able to find the really long discussions that led to the current choice.
Just to clarify, I'm not arguing for or from a certain position, rather, I'm looking for enlightenment as to what the consequences of disallowing the assignment operator for optional<T&> are.
OK. If you consider doing that with boost::optional<>, then the biggest consequence is that is changes the current behaviour. So let's consider the case of an alternative class.
I would be greatly disappointed if you did not follow up to any of my direct queries to you, even if it is just a pointer to look elsewhere.
Well, don't be. I work 12 hours a days (seriously), and have a wife and two kids on top of that, so often I cannot even read this group. If I dissapear it won't mean that I'm not interested in responding, just that I simply can't.
Ok, I think I understand your argument. To clarify, by consistency here you mean consistency of programming in a generic environment, so that, as you say, a generic library would not have to discriminate between T and T&.
Yes
Disclaimer1: What I will say below may have already been discussed, if so, please feel free to tell me to google it. Disclaimer2: I have very little experience with writing generic libraries in the C++ sense. Disclaimer3: Besides the documentation, I have no experience in actually using optional.
What are the consequences of doing away with the assignment operator totally
The consequence is, well, that you cannot assign to it :P Granted, you can get away without assignment to a certain extent, and we might agree that doing so is often a good thing, even in C++ which is not a pure functional language. But, since C++ purposedly supports and embraces other programming paradigms as well, removing assignment alltoghether might not be feaseble, or practical, and in many cases, not even desirable. In the design space that is most natural to C++, assignment is a fundamental operation. boost::optional<> is an utility which is specifically designed to be general purpose, and to that effect it must support a wide range of applications. Doing what you propose will simply reduce, and quite significantly, the usability of it.
To clarify, in explaining my motivation for the intent of this thread I was saying that a casual programmer would expect that the assignment semantics of T would be a subset of the assignment semantics of optional<T>, not the other way around.
In general, for any wrapper type class "wrap" and wrapped type T, I think that a casual programmer would expect the semantics of the "natural operations" of T to be a subset of the semantics of the those same operations wherever defined on wrap. The reasoning being that the casual user of wrap who is accustomed to the conventions of T will not be unduly surprised when he/she applies those same conventions to wrap. And in this sense, there is no inconsistency between T and wrap, since the "natural operations" of T just seem to work with wrap, whenever wrap truly represents its underlying type.
He would expect that, yes. And he would be surprised. And there is an incosistency in the terms you are stating it. However, the behvaiour a library component cannot be based solely, nor even primarily, on what a causal programmer would expect, or what would be suprising. Those are elements to take into consideration. Sometimes, subtelty is justified by the benefits. Take std::auto_ptr just to name a simple example.
Fair, I understand not wanting to sound like a broken tape recorder. IMHO, this indicates a need to add a general "Design Rationale" section to the documentation, maybe incorporating summaries of the discussions that you allude to.
That seems like a good idea. Best -- Fernando Cacciola SciSoft Consulting, Founder http://www.scisoft-consulting.com

On Tue, 30 Aug 2011 22:23:58 -0700, Mostafa <mostafa_working_away@yahoo.com> wrote:
Boost.Optional seems to really have a lot of potential for what I want to do, but one thing about it has been nagging me, specifically it's assignment semantics for wrapped references being different from the semantics of bare C++ references (see http://tinyurl.com/4yna643).
Has/have the author(s) of the library or anyone considered disallowing assignment for Boost.Optional types that wrap references, and if so, what would the consequences of this entail? I rather disallow assignment in this case, then have what's basically a wrapper class behave partially differently than the type it's supposed to emulate.
As promised, attached is a matrix of alternatives to operator= with accompanying pros/cons and supplementary reference to explain in detail what may not be obvious. This matrix and it's examples were culled from the archives and this thread. Are there any alternatives that were discussed but which I omitted from the matrix? Hi Fernando (anybody else that may care to jump in), In this posting: http://tinyurl.com/6k37wrc, you noted that optional<T&> can be replaced by optional<reference_wrapper<T> >, and asked why optional<T&> would still be needed. Gennadiy Rozental responded that it is still needed for generic programming, else he would have to support a special case. Can you enlighten me how this is so, so I can add the rational to the cons section of alt4_optional in the attached doc. (I would think that judicious use of boost::ref/cref would obviate the need for any special cases in generic programming.) Thanks, Mostafa

On Tue, 30 Aug 2011 22:23:58 -0700, Mostafa <mostafa_working_away@yahoo.com> wrote: *Bump* Hi Fernando, may you answer the following query: As promised, attached is a matrix of alternatives to operator= with accompanying pros/cons and supplementary reference to explain in detail what may not be obvious. This matrix and it's examples were culled from the archives and this thread. Are there any alternatives that were discussed but which I missed? Hi Fernando (anybody else that may care to jump in), In this posting: http://tinyurl.com/6k37wrc, you noted that optional<T&> can be replaced by optional<reference_wrapper<T> >, and asked why optional<T&> would still be needed. Gennadiy Rozental responded that it is still needed for generic programming, else he would have to support a special case. Can you enlighten me how this is so, so I can add the rational to the cons section of alt4_optional in the attached doc. (I would have thought that judicious use of boost::ref/cref would obviate the need for any special cases in generic programming.) Thanks, Mostafa

Hi Mostafa, I can't look into this right now but I will try to respond somnetime this week. Best -- --- Fernando Cacciola SciSoft Consulting, Founder http://www.scisoft-consulting.com
participants (6)
-
Edward Diener
-
Fernando Cacciola
-
Jeffrey Lee Hellrung, Jr.
-
Mostafa
-
Nevin Liber
-
Stewart, Robert