std::optional library proposal -- request for feedback

749 views
Skip to first unread message

Andrzej Krzemieński

unread,
Jun 1, 2012, 3:34:28 AM6/1/12
to std-pr...@isocpp.org
Hi,
The link below is the draft of the second revision of the proposal to add Standard Library component for representing optional (nullable) objects, based on Boost.Optional library.
http://kojot.sggw.waw.pl/~akrzemi1/optional/tr2.optional.proposal.html

We would appreciate your feedback.

Regards,
&rzej

Ville Voutilainen

unread,
Jun 1, 2012, 3:57:38 AM6/1/12
to std-pr...@isocpp.org
First of all, thanks! Alisdair mentioned in passing during one of the committee
meetings that we need "vocabulary types", optional being one of them.

I have to admit that having "none" directly under std gives me the creeps. :)
Is it possible to nest it under optional, somehow? I think I can get over it,
though...

Alberto Ganesh Barbati

unread,
Jun 1, 2012, 4:18:50 AM6/1/12
to std-pr...@isocpp.org

Il giorno 01/giu/2012, alle ore 09:57, Ville Voutilainen ha scritto:

> On 1 June 2012 10:34, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>> Hi,
>> The link below is the draft of the second revision of the proposal to add
>> Standard Library component for representing optional (nullable) objects,
>> based on Boost.Optional library.
>> http://kojot.sggw.waw.pl/~akrzemi1/optional/tr2.optional.proposal.html
>> We would appreciate your feedback.
>
> First of all, thanks! Alisdair mentioned in passing during one of the committee
> meetings that we need "vocabulary types", optional being one of them.

Yes, thanks, we definitely need this one too.

> I have to admit that having "none" directly under std gives me the creeps. :)
> Is it possible to nest it under optional, somehow? I think I can get over it,
> though...

+1.

in_place_t is really annoying and the two-line rationale in the proposal leave me unsatisfied. I wish we could elaborate more on the subject. Is it possible that we can't we find some template meta-programming trick to avoid it?

Ganesh

Andrzej Krzemieński

unread,
Jun 1, 2012, 4:20:03 AM6/1/12
to std-pr...@isocpp.org

W dniu piątek, 1 czerwca 2012 09:57:38 UTC+2 użytkownik Ville Voutilainen napisał:
I have to admit that having "none" directly under std gives me the creeps. :)
Is it possible to nest it under optional, somehow? I think I can get over it,
though...

I see your point. I do not think we ever took this argument into account. I do not know of any way of nesting "none" under optional, though. This is because optional is a template (and requires parameters), whereas "none" is not (and should not be). It would be easier id std itself was divided into sub-namespaces. We could change it to "null_optional", or reconsider nullptr for that purpose, but the latter solution also has problems of its own.

Regards,
&rzej

Ville Voutilainen

unread,
Jun 1, 2012, 4:24:18 AM6/1/12
to std-pr...@isocpp.org
On 1 June 2012 11:20, Andrzej Krzemieński <akrz...@gmail.com> wrote:
> I see your point. I do not think we ever took this argument into account. I
> do not know of any way of nesting "none" under optional, though. This is
> because optional is a template (and requires parameters), whereas "none" is

Yep. Hence my "somehow", I couldn't immediately think of a sane way to
nest it.

> not (and should not be). It would be easier id std itself was divided into
> sub-namespaces. We could change it to "null_optional", or reconsider nullptr
> for that purpose, but the latter solution also has problems of its own.

I think the rationale of using none rather than nullptr is quite well explained
in the proposal, and I agree with the rationale, I don't think nullptr
is a viable choice.

Chris Jefferson

unread,
Jun 1, 2012, 5:14:52 AM6/1/12
to std-pr...@isocpp.org
At first glance it looks good, and thank you for dealing with such a complex issue.

I wish that that we had given 'piecewise_construct_t' a more generic name, like 'inplace_t' in C++11. I don't think piecewise_construct_t really make sense as a name, but it would be useful to try to reduce the number of tag names we introduce in future. Hopefully inplace_t can be used for future uses.

Chris

Andrzej Krzemieński

unread,
Jun 1, 2012, 5:21:19 AM6/1/12
to std-pr...@isocpp.org
W dniu piątek, 1 czerwca 2012 10:18:50 UTC+2 użytkownik Alberto Ganesh Barbati napisał:
in_place_t is really annoying and the two-line rationale in the proposal leave me unsatisfied. I wish we could elaborate more on the subject. Is it possible that we can't we find some template meta-programming trick to avoid it?

I am not sure I understand. "Annoying" to library providers that they would have to provide a component that will almost never be used? "Annoying" to programmers (end users of optional) that they will have to type the tag?

I imagine that this construct will hardly ever be used, so it will not disturb the programmers. After all, if you know how to initialize the object from the outset, you probably do not need optional to begin with. But optional might be useful if you want to end the life-time of the object before the end of function/block scope -- DefaultConstructible object:

  void fun()
  {
    optional<GlobalLock> g(in_place);
   
// use g
    g = none;
   
// do other stuff
  }


Here, GlobalLock is a guard-like object with 'meaningful' default constructor. In this example you could introduce an extra scope, but in more complex ones where multiple "artificial" scopes would overlap (the proposal contains such examples) this would not work.

You would rarely have to use such solution, so you would have an inelegant solution for rare problems. I am not sure how "meta-programming" would offer less inelegant solution to the above example.

Regards,
&rzej

Chris Jefferson

unread,
Jun 1, 2012, 5:25:47 AM6/1/12
to std-pr...@isocpp.org
On 01/06/12 09:18, Alberto Ganesh Barbati wrote:
> in_place_t is really annoying and the two-line rationale in the
> proposal leave me unsatisfied. I wish we could elaborate more on the
> subject. Is it possible that we can't we find some template
> meta-programming trick to avoid it? Ganesh

Actually, there might be a much more boring method.

I think that:

optional<T> o(in_place_t, arg1, arg2);

can always be replaced by (with I think no loss of efficiency, assuming
a reasonable optimising compiler)

optional<T> o;
o.emplace(arg1, arg2);

Sure, that does add another line, but it avoids the need for in_place_t.
Unlike pair, where piecewise_construct_t was necessary for types with no
default constructor, here a default-constructed optional just constructs
no object at all.

Chris

Alberto Ganesh Barbati

unread,
Jun 1, 2012, 5:27:39 AM6/1/12
to std-pr...@isocpp.org
piecewise_construct_t and inplace_t have two very different meanings: the former extracts components from a single tuple argument, while the latter uses a variadic list of arguments directly. Therefore I am happy in having a distinction. Just my opinion.

Ganesh

Alberto Ganesh Barbati

unread,
Jun 1, 2012, 5:47:53 AM6/1/12
to std-pr...@isocpp.org
Il giorno 01/giu/2012, alle ore 11:21, Andrzej Krzemieński ha scritto:

W dniu piątek, 1 czerwca 2012 10:18:50 UTC+2 użytkownik Alberto Ganesh Barbati napisał:
in_place_t is really annoying and the two-line rationale in the proposal leave me unsatisfied. I wish we could elaborate more on the subject. Is it possible that we can't we find some template meta-programming trick to avoid it?

I am not sure I understand. "Annoying" to library providers that they would have to provide a component that will almost never be used? "Annoying" to programmers (end users of optional) that they will have to type the tag?

It's annoying because I (user) have to type a tag. I mean, in this case:

struct A
{
   A(int x, double y);
};

I wish I could write:

  optional<A> x(42, 3.14);

rather than

  optional<A> x(in_place, 42, 3.14);

However, it might be even better if we could make:

  optional<A> x({42, 3.14});

work with in-place semantic, as it is way clearer, IMHO. However I guess the language is not going to help us in this direction...

  void fun()
  {
    optional<GlobalLock> g(in_place);
   
// use g
    g = none;
   
// do other stuff
  }


Ah! Ok. This is an example I'd like to see in the proposal. This motivates in_place, because when there's no argument, we have no syntax to disambiguate "have a default-constructed optional<A>" from "have an optional<A> initialized with a default-constructed A". However, I believe this might be the only case. If at least one argument is present, we might be able to disambiguate using some enable_if voodoo or a possibly forthcoming "requires" core language feature.

By the way, given that all standard containers use the name "emplace", wouldn't be nicer to stay consistent and use "emplace_t", given that the semantic is similar?

Ganesh


dave

unread,
Jun 1, 2012, 5:55:25 AM6/1/12
to std-pr...@isocpp.org
On 6/1/12 3:57 AM, Ville Voutilainen wrote:
> On 1 June 2012 10:34, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>> Hi,
>> The link below is the draft of the second revision of the proposal to add
>> Standard Library component for representing optional (nullable) objects,
>> based on Boost.Optional library.
>> http://kojot.sggw.waw.pl/~akrzemi1/optional/tr2.optional.proposal.html
>> We would appreciate your feedback.
> First of all, thanks! Alisdair mentioned in passing during one of the committee
> meetings that we need "vocabulary types", optional being one of them.
+1

Andrzej Krzemieński

unread,
Jun 1, 2012, 6:03:36 AM6/1/12
to std-pr...@isocpp.org
 
The two -- in_place_t and in_place -- constitute a "pair" the first is a type used in optional interface declarations, the other will be used by the users. If I rename one, I guess I also have to rename the other, but name emplace is already taken (and has different type).

But now I think I like Chris's suggestion even better: in the unusual case just initialize in two lines. The only other place where such emplacement constructor would be useful is for temporaries in return statements or complex expressions, but in those cases perhaps an improved variant of make_optional could do.
 
Regards,
&rzej

Chris Jefferson

unread,
Jun 1, 2012, 6:08:57 AM6/1/12
to std-pr...@isocpp.org
On 01/06/12 11:03, Andrzej Krzemieński wrote:

The two -- in_place_t and in_place -- constitute a "pair" the first is a type used in optional interface declarations, the other will be used by the users. If I rename one, I guess I also have to rename the other, but name emplace is already taken (and has different type).

But now I think I like Chris's suggestion even better: in the unusual case just initialize in two lines. The only other place where such emplacement constructor would be useful is for temporaries in return statements or complex expressions, but in those cases perhaps an improved variant of make_optional could do.

Just wanted to point out one thing, back from when something similar happened with std::vector.

At one point there was a suggestion that we didn't really need 'emplace_back', we could just make 'push_back' take a variadic list of arguments and construct from them. This wouldn't break any existing code, which used push_back.

However, the problem which arose was that it meant that code like:

vector<vector<int> > v;
v.push_back(1);

which used to not compile, would now compile and construct a vector of length 1. If changing make_optional, it is probably worth considering how to 'keep users safe' from accidentally invoking constructors they do not want.

Chris

Alisdair Meredith

unread,
Jun 1, 2012, 6:11:03 AM6/1/12
to std-pr...@isocpp.org
Thanks, this is an excellent first draft of a paper!

There seem to be two, somewhat compatible, mental models for an 'optional' type,
a smart pointer whose managed object is stored within the object, or a container
with a maximum capacity of one.

As you noted, the previous time we review such a proposal, the interface tried to
accomodate both views, and this was controversial, so thanks for cleaning up the
interface.

My own view tended towards the container-with-restricted-capacity view, but I
suspect the smart-pointer view that you have followed will appeal to the majority.
However, if we are going the smart pointer route, I think I would prefer to satisfy
the NullablePointerlike requirements and use nullptr_t rather than none_t to
represent the empty state. I buy the arguments for none_t if we are considering
the container-like interface.

Otherwise, the basic design seems solid, with just a few tweaks mentioned by
others such as an 'emplace' function.

AlisdairM

Alisdair Meredith

unread,
Jun 1, 2012, 6:36:44 AM6/1/12
to std-pr...@isocpp.org
On 1Jun, 2012, at 3:34 AM, Andrzej Krzemieński wrote:

Hmm, one new design issue strikes me arising form the C++11 standard.

If the type stored in my optional supports 'uses-allocator construction' then
I want that optional object to also support uses-allocator construction. For
example, this will matter for containers of optional object.

If the target type does not support 'uses-allocator construction' then that
optional instantiation probably wants to set the trait to 'false' and SFINAE
away the constructors.

I am going to try to model this over the weekend before making specific
suggestions - there is no substitute for working code <g>.

Example:

template<typename T> MyAllocator;
template<typename T> using MyAlloc = scoped_allocator_adapter<MyAllocator<T>>;
template<typename T> using MyVector = std::vector<T, MyAlloc<T>>;

MyVector<optional<MyVector<double>>> x{10, {10, 1.618}, commonAllocator};

In this case, 'commonAllocator' should be used to construct the allocator for 11 vectors.

AlisdairM

Andrzej Krzemieński

unread,
Jun 1, 2012, 7:02:09 AM6/1/12
to std-pr...@isocpp.org

W dniu piątek, 1 czerwca 2012 12:11:03 UTC+2 użytkownik Alisdair Meredith napisał:

Thanks, this is an excellent first draft of a paper!

There seem to be two, somewhat compatible, mental models for an 'optional' type,
a smart pointer whose managed object is stored within the object, or a container
with a maximum capacity of one.

As you noted, the previous time we review such a proposal, the interface tried to
accomodate both views, and this was controversial, so thanks for cleaning up the
interface.

My own view tended towards the container-with-restricted-capacity view, but I
suspect the smart-pointer view that you have followed will appeal to the majority.
However, if we are going the smart pointer route, I think I would prefer to satisfy
the NullablePointerlike requirements and use nullptr_t rather than none_t to
represent the empty state.  I buy the arguments for none_t if we are considering
the container-like interface.

Otherwise, the basic design seems solid, with just a few tweaks mentioned by
others such as an 'emplace' function.
 
Let me just remark that this is a second draft (of the second revision), much improved owing to exhaustive and insightful input form Daniel Kruegler.

When it comes to the mental model behind optional, we tend to think about it as a "discriminated union of types none_t and T" or, if I can use Boost's nomenclature:

variant<none_t, T>

We never thought of it as a pointer anymore than we liked operators -> and * for indicating the access to the optionally contained value. When it provides deep copy semantics and does not allow you to create the owned value yourself, can we still cal such type a pointer?

Regards,
&rzej

Nevin Liber

unread,
Jun 1, 2012, 12:14:45 PM6/1/12
to std-pr...@isocpp.org

+1 on the need for optional.

At brief glance:

"Copy assignment of optional<T> provides the same exception guarantee as copy assignment of T. Move assignment of optional<T> provides the same exception guarantee as move assignment of T."

Is that true?  Isn't the exception guarantee for copy [move] assignment of optional<T> the combination of copy [move] construction of T and copy [move] assignment of T, dependent on the engaged / disengaged state of the assignee before the assignment?
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

Jeffrey Yasskin

unread,
Jun 1, 2012, 1:01:00 PM6/1/12
to std-pr...@isocpp.org, akrz...@gmail.com
The proposal overall looks great, and I'm really looking forward to
having an optional type in the standard library.

I'm skeptical about optional<T&>: In Boost's experience, does it get
used in practice? Does anyone get confused by it? (If the answers are
'yes' and 'no', then I have no objections, but otherwise it would be
nice to simplify the spec a bit by disallowing it.)


It would also be nice to think about ways to optimize the storage.
There are plenty of types that have a few spare internal bits, which
could be used to hold the "initialized" bool. For raw pointers, the
implementation could just use the bits, but for structs of pointers,
it generally can't. We could, in theory, define a trait saying how
many spare bits there are, with operations to split the bits from the
original value. LLVM has a trait similar to this, but it's pretty
specialized to pointers:
http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/PointerLikeTypeTraits.h?view=markup.

Such a trait may be more useful for variants than optionals, and could
probably be added later, after optional is adopted.

Thanks for writing this up,
Jeffrey

Chris Jefferson

unread,
Jun 1, 2012, 1:10:13 PM6/1/12
to std-pr...@isocpp.org
On 01/06/12 18:01, Jeffrey Yasskin wrote:
>
> Such a trait may be more useful for variants than optionals, and could
> probably be added later, after optional is adopted.

I have done something like this in my personal implementation of
optional. I define a class:

template<typename T>
struct checkOptional
{
static bool valueAbsent(void* v);
static void setAbsent(void* v);
};

The idea being that when checkOptional is defined, then optional<T>
contains only a buffer of size sizeof(T). The setAbsent puts the memory
in a state which a T can never take, and valueAbsent checks is that
state is present.

It is possible (but I would not put in the standard) to define
implementations of checkOptional for pointers and floats and doubles,
under some reasonable assumptions (I know I will never have a pointer to
memory location 1, and I can choose one of the NaN representations).

It is much easier to define implementations of checkOptional (given
internal knowledge) for most implementations of the standard containers
(for example).

My main concern (and something I am not an expert on) is how to define
this in such a way that it does not break aliasing issues. I suspect I
might be doing some very dodgy things, and worry that standardising such
a thing would greatly complicate a nice and simple idea.

Chris

Nevin Liber

unread,
Jun 1, 2012, 2:20:58 PM6/1/12
to std-pr...@isocpp.org
On 1 June 2012 12:01, Jeffrey Yasskin <jyas...@googlers.com> wrote:
The proposal overall looks great, and I'm really looking forward to
having an optional type in the standard library.

I'm skeptical about optional<T&>: In Boost's experience, does it get
used in practice? Does anyone get confused by it? (If the answers are
'yes' and 'no', then I have no objections, but otherwise it would be
nice to simplify the spec a bit by disallowing it.)

I'm pretty sure I've used it in the past, but no longer have access to that source base to check definitively.

That being said, it should be allowed.  Anything that is "special" is a pain to use in generic programming contexts, as you end up having to check for the "special" type and do something different (vector<bool> being the poster child of this problem).
 
It would also be nice to think about ways to optimize the storage.
There are plenty of types that have a few spare internal bits, which
could be used to hold the "initialized" bool. For raw pointers, the
implementation could just use the bits, but for structs of pointers,
it generally can't. We could, in theory, define a trait saying how
many spare bits there are, with operations to split  the bits from the
original value. LLVM has a trait similar to this, but it's pretty
specialized to pointers:
http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/PointerLikeTypeTraits.h?view=markup.

I'm not sure you can do this in general, as you don't know if the user is using the pointer to access an object or just for identity.  Even if you can, this is just an implementation detail and should not be specified by the standard (or part of a different proposal if it is something more general).

Fernando Cacciola

unread,
Jun 1, 2012, 2:30:55 PM6/1/12
to std-pr...@isocpp.org
Hi Andrzej,


However, it might be even better if we could make:

  optional<A> x({42, 3.14});

Looking at your reference implementation, doesn't the example above work precisely as intended (doing in_place construction of the value)?

If not, I think we should figure out a way to make it so. And then just remove in_place_t entirely.

Best

Fernando Cacciola

unread,
Jun 1, 2012, 2:46:55 PM6/1/12
to std-pr...@isocpp.org, akrz...@gmail.com

I'm skeptical about optional<T&>: In Boost's experience, does it get
used in practice? Does anyone get confused by it? (If the answers are
'yes' and 'no', then I have no objections, but otherwise it would be
nice to simplify the spec a bit by disallowing it.)



Andrzej: could you post here a link to that page where you collected, assembled and categorized all the previous important discussions about Boost.Optional?  

The best way to answer a fundamental concern like the one raised by Jeffrey is to direct him to the relevant discussion that already took place. Otherwise we risk entering the bycicle shed once again.

TIA


Jeffrey Yasskin

unread,
Jun 1, 2012, 3:02:37 PM6/1/12
to std-pr...@isocpp.org, akrz...@gmail.com
+1

Nevin's generic-programming reason isn't that convincing to me, since
references already require so much special-case code, but seeing
examples of how it's been used could easily convince me.

Thanks,
Jeffrey

Fernando Cacciola

unread,
Jun 1, 2012, 3:18:10 PM6/1/12
to std-pr...@isocpp.org, akrz...@gmail.com
FWIW, totally removing support for references, as you suggest, has been considered several times. I've entertained the idea and surely have been, and still am, tempted to just get rid of it. But in the end, sufficient uses cases pop up (for instance, this proposal contains one). In fact, Andrzej idea of removing assignment but keeping support for simpler immutable semantics is an attempt to find the right balance. IMO, it is a sensible compromise.

Best

--
Fernando Cacciola
SciSoft Consulting, Founder
http://www.scisoft-consulting.com

Andrzej Krzemieński

unread,
Jun 1, 2012, 3:31:47 PM6/1/12
to std-pr...@isocpp.org
At brief glance:

"Copy assignment of optional<T> provides the same exception guarantee as copy assignment of T. Move assignment of optional<T> provides the same exception guarantee as move assignment of T."

Is that true?  Isn't the exception guarantee for copy [move] assignment of optional<T> the combination of copy [move] construction of T and copy [move] assignment of T, dependent on the engaged / disengaged state of the assignee before the assignment?

You are right. This is a bug in the overview section. The wording, though, got that they way you described.

Andrzej Krzemieński

unread,
Jun 1, 2012, 3:54:51 PM6/1/12
to std-pr...@isocpp.org, akrz...@gmail.com

Beman Dawes

unread,
Jun 1, 2012, 3:57:11 PM6/1/12
to std-pr...@isocpp.org
On Fri, Jun 1, 2012 at 3:34 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:
> Hi,
> The link below is the draft of the second revision of the proposal to add
> Standard Library component for representing optional (nullable) objects,
> based on Boost.Optional library.
> http://kojot.sggw.waw.pl/~akrzemi1/optional/tr2.optional.proposal.html
>
> We would appreciate your feedback.

+1.

Consider adding an short introductory section before the Table of
Contents. Perhaps something like this:

Introduction

Class template optional<T> in effect supplies a discriminated union of
types T and none_t allocated in the same storage space. The current
contents can be determined; i.e. either a value of type T or the
special value none. The interface is based on Fernando Cacciola's
Boost.Optional library[2], shipping since March, 2003, and is widely
used. It requires no changes to standard library headers, and breaks
no existing code (modulo the usual using namespace std caveats.)

Table of Contents
...

Thanks for a very nice proposal indeed!

--Beman

Andrzej Krzemieński

unread,
Jun 1, 2012, 4:19:25 PM6/1/12
to std-pr...@isocpp.org, akrz...@gmail.com

W dniu piątek, 1 czerwca 2012 19:01:00 UTC+2 użytkownik Jeffrey Yasskin napisał:
I'm skeptical about optional<T&>: In Boost's experience, does it get
used in practice? Does anyone get confused by it? (If the answers are
'yes' and 'no', then I have no objections, but otherwise it would be
nice to simplify the spec a bit by disallowing it.)

"Does it get used in practice?" -- yes, although an order of magnitude less frequently than optional<T>. Even recently I had to use it. My function had the following signature:
  bool process(Arg arg, Result & result);
result
being an output argument, and in one place I observed that there was no result I could provide, so I decided to make it an optional reference and simply not write to it if it was not provided. Using a pointer output argument instead would give a similar result, but then I would have to go back to every place that uses process, and add the addressof operator. Using optional reference made my changes less intrusive.

"Does anyone get confused by it?" -- yes, people get confused by the semantics of assignment. Some people naturally expect that the assignment would rebind the reference, and some naturally expect the opposite. The goal of this proposal was to move the surprise from run-time to compile-time .

 

Andrzej Krzemieński

unread,
Jun 1, 2012, 4:25:50 PM6/1/12
to std-pr...@isocpp.org
 
I am not sure if you and Jeffrey talk about exactly the same kind of optimization. I believe that in case of types that the Implementation is aware of, like pointers, bools, doubles, it can already provide an optimization for optional without any requirement from the Standard. Shouldn't that be the quality-of-implementation issue?
The case is different for user-defined types though.

Andrzej Krzemieński

unread,
Jun 1, 2012, 4:36:40 PM6/1/12
to std-pr...@isocpp.org

I will still need to check that, but even if works, there is one essential difference between:


  optional<A> x(in_place, 42, 3.14);


and

  optional<A> x({42, 3.14});

The latter requires the creation of a temporary and copy or move construction. It is equivalent to:
 
  optional<A> x( A{42, 3.14} ); // or
  optional<A> x( A(42, 3.14) );

And our type A could be non-moveable (like a scope guard).
What we could do is to provide a perfect-forwarding constructor (and somehow handle the special cases):
 
  optional<A> x(42, 3.14);


But then the problem remains, how to construct an engaged (initialized) optional by calling A's default constructor.

Fernando Cacciola

unread,
Jun 1, 2012, 5:28:51 PM6/1/12
to std-pr...@isocpp.org

And our type A could be non-moveable (like a scope guard).

Ah, of course. For a moment I considered move construction to have the same net effect of emplace construction, but that of course is not neccesarily true, and definitely false for non-moveable types.

 
What we could do is to provide a perfect-forwarding constructor (and somehow handle the special cases):
 
  optional<A> x(42, 3.14);


But then the problem remains, how to construct an engaged (initialized) optional by calling A's default constructor.

I think the best choice is to support direct perfect-forwarding (without in_place), and support the later via a named constructor:

optional<int> o = optional_int<int>::default_constructed_value() ;

It is a little verbose (not to me, but I'm the minority), but is such a corner case that I don't think it really matters the verbosity as long as the feature is provided.

Best

 

Fernando Cacciola

unread,
Jun 1, 2012, 5:32:22 PM6/1/12
to std-pr...@isocpp.org
  
  optional<A> x(42, 3.14);


FWIW, this was supported in the class that originated the Boost.Optional submission. For the boost version I removed it because lacking perfect forwarding it wasn't sufficiently generic.
OTOH, when I used it in my own code it was quite useful and elegant.

Best 

Andrzej Krzemieński

unread,
Jun 2, 2012, 4:29:56 AM6/2/12
to std-pr...@isocpp.org


W dniu piątek, 1 czerwca 2012 23:28:51 UTC+2 użytkownik Fernando Cacciola napisał:
What we could do is to provide a perfect-forwarding constructor (and somehow handle the special cases):
 
  optional<A> x(42, 3.14);


But then the problem remains, how to construct an engaged (initialized) optional by calling A's default constructor.

I think the best choice is to support direct perfect-forwarding (without in_place), and support the later via a named constructor:

optional<int> o = optional_int<int>::default_constructed_value() ;

It is a little verbose (not to me, but I'm the minority), but is such a corner case that I don't think it really matters the verbosity as long as the feature is provided.

I find this solution nice, and in the spirit of the current proposal. I.e., we already have other uniformity problems when it comes to default-constructing the contained value:

  vector<int> v1 = {1, 2, 3}; // 3-element vector
  vector<int> v2 = {}; // 0-element vector
  optional<vector<int>> o1 = {1, 2, 3}; // 3-element vector
  optional<vector<int>> o2 = {}; // disengaged optional

Thus, default-construction of the contained value would have to become a known gotcha. But, as you say, this is a rare case, because if type A has a default constructor it probably provides some concept of optionality already. I think that when it comes to providing a special syntax for default value initialization, it can be simplified a bit so that we do not require specifying the template argument type:

 optional<int> o = optional_default_constructed_value;

But then, it is almost no different than using tag in_place.
I guess we could provide both perfect-forwarding constructor and the "in place" constructor: just in case we need to resolve some ambiguities.

Dave Abrahams

unread,
Jun 2, 2012, 7:14:51 AM6/2/12
to std-pr...@isocpp.org
On Sat, Jun 2, 2012 at 4:29 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>
> I find this solution nice, and in the spirit of the current proposal. I.e.,
> we already have other uniformity problems when it comes to
> default-constructing the contained value:
>
>   vector<int> v1 = {1, 2, 3}; // 3-element vector
>   vector<int> v2 = {}; // 0-element vector
>   optional<vector<int>> o1 = {1, 2, 3}; // 3-element vector
>   optional<vector<int>> o2 = {}; // disengaged optional

I probably misssed it somewhere back there in the flood of earlier
messages, but is there a good reason (other than lack of usage
experience) not to bring uniformity back by making construction of the
empty state the one that's explicit?

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Nevin Liber

unread,
Jun 2, 2012, 3:38:16 PM6/2/12
to std-pr...@isocpp.org
On 2 June 2012 06:14, Dave Abrahams <da...@boostpro.com> wrote:
I probably misssed it somewhere back there in the flood of earlier
messages, but is there a good reason (other than lack of usage
experience) not to bring uniformity back by making construction of the
empty state the one that's explicit?

+1.
-- 

Nevin Liber

unread,
Jun 2, 2012, 3:41:58 PM6/2/12
to std-pr...@isocpp.org
On 2 June 2012 03:29, Andrzej Krzemieński <akrz...@gmail.com> wrote:

, default-construction of the contained value would have to become a known gotcha. But, as you say, this is a rare case, because if type A has a default constructor it probably provides some concept of optionality already.

It isn't rare at all.  If you are modeling the result of a database query, set/not set is very different than what the value happens to be.

Andrzej Krzemieński

unread,
Jun 2, 2012, 4:23:49 PM6/2/12
to std-pr...@isocpp.org


W dniu sobota, 2 czerwca 2012 21:41:58 UTC+2 użytkownik Nevin ":-)" Liber napisał:
On 2 June 2012 03:29, Andrzej Krzemieński <akrz...@gmail.com> wrote:

, default-construction of the contained value would have to become a known gotcha. But, as you say, this is a rare case, because if type A has a default constructor it probably provides some concept of optionality already.

It isn't rare at all.  If you are modeling the result of a database query, set/not set is very different than what the value happens to be.

I am not sure I understand what you are trying to say. While in principle I agree with you, I cannot see how it shows that the need to initialize optional object with the default constructor of T "isn't rare at all." Could you elaborate on this?

On the other hand perhaps I did not express myself clearly. Let me show some examples of what I mean. Consider class std::thread. It provides a default constructor which creates an object that represents a "not-a-thread" value. It is as though it had a "built-in" optional. Therefore using construction:

  optional<thread> t(in_place); // force thread's default constructor

is suspicious. What you probably needed was:

  thread t;

Similarly, in case of int, you only need its default constructor for performance reasons. Otherwise zero initialization is probably better because it makes the program more predictable (no UB when you read uninitialized value). So, when you need this super-performance, you will probably not use optional anyway. Otherwise, when you create automatic object like this:

   optional<int> i(in_place); // force thread's default constructor

It is suspicious, because you either want to signal that you do not have a good value yet, and in this case, you should have rather written:

  optional<int> i(none); // a disengaged optional

or, you really meant to store value zero, which you could have spelled out:

  optional<int> i (0);
 

Andrzej Krzemieński

unread,
Jun 2, 2012, 4:35:44 PM6/2/12
to std-pr...@isocpp.org

One of the design goals of optional is that default constructor is cheap. Owing to that you are guaranteed that declarations like the following do not cost you much at run-time:

class ExpensiveDefaultCtor;
ExpensiveDefaultCtorarray[100]; // 100 default constructions
std::array<ExpensiveDefaultCtor, 100> arr; // 100 default constructions
std::vector<ExpensiveDefaultCtor> vec(100); // 100 default constructions

Also, we tend to make optional a Regular type in STL sense (at least how I understand the concept), where default-constructed state should guarantee no more than moved-from state. For instance function resize needs to call some constructor, but we need it to be as cheap as possible because next thing that the user will do is to overwrite the current value.
It is possible that T's default constructor is also cheap already and creates a value that means "not-a-T" (like std::thread), but in that case you probably need to use type T rather than optional<T>.


Andrzej Krzemieński

unread,
Jun 2, 2012, 4:51:12 PM6/2/12
to std-pr...@isocpp.org


W dniu piątek, 1 czerwca 2012 09:34:28 UTC+2 użytkownik Andrzej Krzemieński napisał:
Hi,
The link below is the draft of the second revision of the proposal to add Standard Library component for representing optional (nullable) objects, based on Boost.Optional library.
http://kojot.sggw.waw.pl/~akrzemi1/optional/tr2.optional.proposal.html

We would appreciate your feedback.

There is one other thing I wanted to get feedback for in this group. At the time Optional was being added to Boost, Boost contained other algebraic data types: Tuple and Variant. One of the design goals for optional was that Optional had an interface "compatile" with Tuple and Variant. For instance, an important question to be asked was if optional<T> should have the same behaviour and (part of) the interface as variant<none_t, T>. In case of our proposal, Variant is not part of STD, nor do we know of any proposal to add it. But should we still take into consideration the fact that Variant might at some point be proposed and Optional should be compatible with it? This might have some implications on the design. For instance, none_t (which could be renamed to something less scarry, like nothing_t) would be promoted to a decent type rather than a tag, and could be used to build "optimized compositions" of algebraic types, e.g. variant<none_t, T, U> in place of optional<variant<T, U>>.

Regards,
&rzej

Dave Abrahams

unread,
Jun 2, 2012, 6:28:48 PM6/2/12
to std-pr...@isocpp.org

on Sat Jun 02 2012, Andrzej Krzemieński <akrzemi1-AT-gmail.com> wrote:

>> On Sat, Jun 2, 2012 at 4:29 AM, Andrzej Krzemieński < akrz...@gmail.com> wrote:
>> >
>> > I find this solution nice, and in the spirit of the current proposal. I.e.,
>> > we already have other uniformity problems when it comes to
>> > default-constructing the contained value:
>> >
>> > vector<int> v1 = {1, 2, 3}; // 3-element vector
>> > vector<int> v2 = {}; // 0-element vector
>> > optional<vector<int>> o1 = {1, 2, 3}; // 3-element vector
>> > optional<vector<int>> o2 = {}; // disengaged optional
>>
>> I probably misssed it somewhere back there in the flood of
>> earlier messages, but is there a good reason (other than lack of
>> usage experience) not to bring uniformity back by making
>> construction of the empty state the one that's explicit?
>>
>>
>> One of the design goals of optional is that default constructor is
>> cheap.

Why is that a desirable goal? Does its desirability outweigh the
non-uniformity?

>> Owing to that you are guaranteed that declarations like the following
>> do not cost you much at run-time:
>>
>> class ExpensiveDefaultCtor;
>>
>> ExpensiveDefaultCtor array[100]; // 100 default constructions
>> std::array<ExpensiveDefaultCtor, 100> arr; // 100 default constructions
>> std::vector<ExpensiveDefaultCtor> vec(100); // 100 default constructions

Huh? I presume ExpensiveDefaultCtor is meant to be a type with an
expensive default constructor. If so, you're going to do 300 expensive
default constructions. I don't see what any of this has to do with the
design of optional.

>> Also, we tend to make optional a Regular type in STL sense (at least
>> how I understand the concept), where default-constructed state should
>> guarantee no more than moved-from state.

You misunderstand that constraint. That is a constraint on the user of
the Regular type model, who doesn't know the specific type with which
he's dealing. There's nothing to prevent the type's author from
guaranteeing more for the default constructed state than for the
moved-from state. Furthermore, there's no way to make optional<T> a
regular type if T is not also a regular type. So default constructing T
would not in any way disturb regularity.

>> For instance function resize needs to call some constructor, but we
>> need it to be as cheap as possible because next thing that the user
>> will do is to overwrite the current value.

I disagree strongly with that assertion. We don't *need* optional<T>{}
to be cheap when T{} is not. We need optional<T> to be well-behaved
with respect to construction, and especially in the presence of variadic
templates, introducing a non-uniformity like the one we're describing is
most undesirable. If you want to ensure empty optionals, just use

v.resize(10, empty_optional)

>> It is possible that T's default constructor is also cheap already and
>> creates a value that means "not-a-T" (like std::thread), but in that
>> case you probably need to use type T rather than optional<T>.

Sorry, I don't know what you're getting at here.

Dave Abrahams

unread,
Jun 2, 2012, 6:31:15 PM6/2/12
to std-pr...@isocpp.org

on Sat Jun 02 2012, Andrzej Krzemieński <akrzemi1-AT-gmail.com> wrote:

> W dniu piątek, 1 czerwca 2012 09:34:28 UTC+2 użytkownik Andrzej
> Krzemieński napisał:
>
> Hi,
> The link below is the draft of the second revision of the
> proposal to add Standard Library component for representing
> optional (nullable) objects, based on Boost.Optional library.
> http://kojot.sggw.waw.pl/~akrzemi1/optional/tr2.
> optional.proposal.html
>
> We would appreciate your feedback.
>
>
> There is one other thing I wanted to get feedback for in this group.
> At the time Optional was being added to Boost, Boost contained other
> algebraic data types: Tuple and Variant. One of the design goals for
> optional was that Optional had an interface "compatile" with Tuple
> and Variant. For instance, an important question to be asked was if
> optional<T> should have the same behaviour and (part of) the
> interface as variant<none_t, T>. In case of our proposal, Variant is
> not part of STD, nor do we know of any proposal to add it.

That must be fixed.

> But should we still take into consideration the fact that Variant
> might at some point be proposed and Optional should be compatible with
> it?

For some undefined definition of "compatible," yes. :-)

> This might have some implications on the design. For instance, none_t
> (which could be renamed to something less scarry, like nothing_t)
> would be promoted to a decent type rather than a tag, and could be
> used to build "optimized compositions" of algebraic types, e.g.
> variant<none_t, T, U> in place of optional<variant<T, U>>.

I think compatibility with variant should be considered, yes. Thanks
for bringing it up.

vb.ma...@gmail.com

unread,
Jun 2, 2012, 9:23:28 PM6/2/12
to std-pr...@isocpp.org
Andrzej, Thank you for having the courage and dedication to step forward with the proposal. I'd personally be delighted to see it in the std namespace. Started reading and the very first chapter and line (which is important IMHO to set the tone) got me uneasy. "Explicit tracking of object's initialization state. Some types are capable of tracking their initialization state: they provide a default constructor which initializes the object to the null-state." Please take my rumblings with a good pinch of salt and the assurance that I had no intention of being rude, imposing, preaching or anything of that sort.

"tracking" does not strike me as the right word as it stands for "following and pursuing". Especially "tracking their initialization state". There is (IMO) nothing to track and, there is no initialization state (IMO) -- objects are in uninitialized (built-ins) or initialized state. Indeed, some types have the luxury to be initialized to some special value to indicate that no meaningful value has been assigned to the object yet (nullptr, NaN, std::unique_ptr()). Still, I'd rather call that the no-meaningful-value state (or no-value state or none-state) rather than initialization state.

Also I feel that "they provide a default constructor which initializes the object to the null-state" makes (and subconsciously cements) the wrong impression that the default constructor is somehow reserved for that special no-value (none-state) initialization. Indeed, some types can afford using the default constructors for such purpose (like std::unique_ptr() can be read as the unique_ptr to nothing). However, many types deploy the default constructors to create legitimate objects. Say, can one confidently say if railway::signal() creates a no-value or some-value object? So, I'd probably be happier with something like "No-Value State. Some types have the capacity of indicating that no meaningful value has been assigned to the respective object yet."

Regards,
V.



On Friday, June 1, 2012 5:34:28 PM UTC+10, Andrzej Krzemieński wrote:
Hi,
The link below is the draft of the second revision of the proposal to add Standard Library component for representing optional (nullable) objects, based on Boost.Optional library.
http://kojot.sggw.waw.pl/~akrzemi1/optional/tr2.optional.proposal.html

We would appreciate your feedback.

Regards,
&rzej

vb.ma...@gmail.com

unread,
Jun 3, 2012, 1:29:21 AM6/3/12
to std-pr...@isocpp.org
On Sunday, June 3, 2012 8:28:48 AM UTC+10, Dave Abrahams wrote:
on Sat Jun 02 2012, Andrzej Krzemieński <akrzemi1-AT-gmail.com> wrote:
>>     On Sat, Jun 2, 2012 at 4:29 AM, Andrzej Krzemieński <     akrz...@gmail.com> wrote:
>>     > I find this solution nice, and in the spirit of the current proposal. I.e.,
>>     > we already have other uniformity problems when it comes to
>>     > default-constructing the contained value:
>>     >
>>     >   vector<int> v1 = {1, 2, 3}; // 3-element vector
>>     >   vector<int> v2 = {}; // 0-element vector
>>     >   optional<vector<int>> o1 = {1, 2, 3}; // 3-element vector
>>     >   optional<vector<int>> o2 = {}; // disengaged optional
>>    
>>     I probably missed it somewhere back there in the flood of
>>     earlier messages, but is there a good reason (other than lack of
>>     usage experience) not to bring uniformity back by making
>>     construction of the empty state the one that's explicit?
>>
>> One of the design goals of optional is that default constructor is
>> cheap.
 
I'd like to strongly second Dave's view on the matter. Uniformity is important as it breeds predictability and confidence. More so, if optional is to provide perfect forwarding, then I would expect that to include the def. constructor. A no-value optional can (and should IMO) be created explicitly. Like

optional<vector<int>> o2 = boost::none; // disengaged optional

Or something of that sort. That looks attractive because 1) optional does not interfere with the value-type API (i.e. it does not hijack the def. cnstr); 2) it is a good programming style where one says what he means (i.e. the code *is* the documentation). All IMHO of course.

Best,
V.
 

Marc Glisse

unread,
Jun 3, 2012, 3:23:41 PM6/3/12
to std-pr...@isocpp.org
On Saturday, June 2, 2012 10:51:12 PM UTC+2, Andrzej Krzemieński wrote:
There is one other thing I wanted to get feedback for in this group. At the time Optional was being added to Boost, Boost contained other algebraic data types: Tuple and Variant. One of the design goals for optional was that Optional had an interface "compatile" with Tuple and Variant. For instance, an important question to be asked was if optional<T> should have the same behaviour and (part of) the interface as variant<none_t, T>. In case of our proposal, Variant is not part of STD, nor do we know of any proposal to add it. But should we still take into consideration the fact that Variant might at some point be proposed and Optional should be compatible with it? This might have some implications on the design. For instance, none_t (which could be renamed to something less scarry, like nothing_t) would be promoted to a decent type rather than a tag, and could be used to build "optimized compositions" of algebraic types, e.g. variant<none_t, T, U> in place of optional<variant<T, U>>.

I was actually surprised to see a proposal for optional before/separately from one for variant. As far as I understand, optional is a special case of variant with a few interfaces added for convenience. I really think we want variant and optional in the standard, and their tight relation makes it hard to judge them separately. I do hope this sounds more like an encouragement for a variant proposal than discouragement for the optional one.

Andrzej Krzemieński

unread,
Jun 3, 2012, 4:18:42 PM6/3/12
to std-pr...@isocpp.org


W dniu niedziela, 3 czerwca 2012 03:23:28 UTC+2 użytkownik vb.ma...@gmail.com napisał:
Andrzej, Thank you for having the courage and dedication to step forward with the proposal. I'd personally be delighted to see it in the std namespace. Started reading and the very first chapter and line (which is important IMHO to set the tone) got me uneasy. "Explicit tracking of object's initialization state. Some types are capable of tracking their initialization state: they provide a default constructor which initializes the object to the null-state." Please take my rumblings with a good pinch of salt and the assurance that I had no intention of being rude, imposing, preaching or anything of that sort.

"tracking" does not strike me as the right word as it stands for "following and pursuing". Especially "tracking their initialization state". There is (IMO) nothing to track and, there is no initialization state (IMO) -- objects are in uninitialized (built-ins) or initialized state. Indeed, some types have the luxury to be initialized to some special value to indicate that no meaningful value has been assigned to the object yet (nullptr, NaN, std::unique_ptr()). Still, I'd rather call that the no-meaningful-value state (or no-value state or none-state) rather than initialization state.

Also I feel that "they provide a default constructor which initializes the object to the null-state" makes (and subconsciously cements) the wrong impression that the default constructor is somehow reserved for that special no-value (none-state) initialization. Indeed, some types can afford using the default constructors for such purpose (like std::unique_ptr() can be read as the unique_ptr to nothing). However, many types deploy the default constructors to create legitimate objects. Say, can one confidently say if railway::signal() creates a no-value or some-value object? So, I'd probably be happier with something like "No-Value State. Some types have the capacity of indicating that no meaningful value has been assigned to the respective object yet."

Thank you for this comment. I will try to rehrase the proposal to avoid any misunderstanding. On the other hand we did want to convey some important information: that you can always query the optional object if it has been assigned value yet. In contrast, you cannot do it with type int. For instance:

  void print(const int& i)
  {
    cout << i << endl;
  }


If this prints "1191" you do not know if this is because someone assigned value 1191 to i or if you are accessing an uninitialized int. And there is no way for you to know that. I will try to come up with better wording for that.

Andrzej Krzemieński

unread,
Jun 3, 2012, 5:23:41 PM6/3/12
to std-pr...@isocpp.org


W dniu niedziela, 3 czerwca 2012 00:28:48 UTC+2 użytkownik Dave Abrahams napisał:

on Sat Jun 02 2012, Andrzej Krzemieński <akrzemi1-AT-gmail.com> wrote:

>>     On Sat, Jun 2, 2012 at 4:29 AM, Andrzej Krzemieński <     akrz...@gmail.com> wrote:
>>     >
>>     > I find this solution nice, and in the spirit of the current proposal. I.e.,
>>     > we already have other uniformity problems when it comes to
>>     > default-constructing the contained value:
>>     >
>>     >   vector<int> v1 = {1, 2, 3}; // 3-element vector
>>     >   vector<int> v2 = {}; // 0-element vector
>>     >   optional<vector<int>> o1 = {1, 2, 3}; // 3-element vector
>>     >   optional<vector<int>> o2 = {}; // disengaged optional
>>    
>>     I probably misssed it somewhere back there in the flood of
>>     earlier messages, but is there a good reason (other than lack of
>>     usage experience) not to bring uniformity back by making
>>     construction of the empty state the one that's explicit?
>>
>>
>> One of the design goals of optional is that default constructor is
>> cheap.

Why is that a desirable goal?  Does its desirability outweigh the
non-uniformity?

Boost Optional as well as this proposal was designed to solve more than one problem. (I do not necessarily say that it is a good thing to do.) The primary goal is to have a compound value that represents either T or nothing (variant<none_t, T>). Another goal is these special guarantees for default constructor. Another one is what optional references try to achieve. If we remove these guarantees for default constructor (and even optional references) optional would still be useful an addition, but I feel uncomfortable removing cheap default constructor just yet -- although I find the non-uniformity argument important. Let me try to defend it.

Optional, as proposed currently, allows you -- in certain cases -- to make your non-regular type regular. Imagine class Date, which stores dates like 12-DEC-2012. It is moveable, copyable assignable, even less-than comparable, but it does not have a default constructor because there is no meaningful default date. The default constructor could create "not-a-date" or a "null-date" , but such choice has its own problems. In such cases optional<Date> creates a fully regular type.

Or more generally: We guarantee that that optional<T> is default-constructible even if T is not. Even more, we guarantee that for global objects its optional's default constructor is initialized during static initialization phase regardless of what T's default-constructor (if provided) guarantees.

We allow you to default-initialize fixed-size containers and skip expensive initialization. My previous example was blindly yanked from the proposal and made little sense so let me try again:

  // the problem:
  ExpensiveDefaultCtor array[100]; // 100 expensive default constructions
  std::array<ExpensiveDefaultCtor, 100> arr; // 100 default constructions 
  std::vector<ExpensiveDefaultCtor> vec(100); // 100 default constructions

  // the solution:
  optional<ExpensiveDefaultCtor> array[100]; // 100 cheap default constructions
  array<optional<ExpensiveDefaultCtor>, 100> arr;
  vector<optional<ExpensiveDefaultCtor>> vec(100);

There is also some psychological argument. I am not sure if you feel the same way. If you read the following lines:

  optional<Model> m;
  // ...
  m = Model(params);

Would you have expected that it actually initialized the Model twice? Especially for globals, I would think it is common to write just:

  optional<Model> m;

Which says "for now, initialize statically to 'not-a-model',I will initialize properly in main when I have read the config file."

The non-uniformity problem could be solved the other way around: disallow any perfect forwarding constructors. To me, they do not appear that essential. You can always use solutions that require an extra move constructor:

opional<Container> compute()
{
  if (...) {
    return
Container{};
  }
  else {
    return none;
  }
}

The rare cases where type T is not moveable, are rare enough to be handled by more clumsy "in_place" constructor.



>> Owing to that you are guaranteed that declarations like the following
>> do not cost you much at run-time:
>>
>> class ExpensiveDefaultCtor;
>>
>> ExpensiveDefaultCtor array[100]; // 100 default constructions
>> std::array<ExpensiveDefaultCtor, 100> arr; // 100 default constructions
>> std::vector<ExpensiveDefaultCtor> vec(100); // 100 default constructions

Huh?  I presume ExpensiveDefaultCtor is meant to be a type with an
expensive default constructor.  If so, you're going to do 300 expensive
default constructions.  I don't see what any of this has to do with the
design of optional.

Sorry for the confusion. I copy-n-pasted too hastily. I provided the correct example above.
 

>> Also, we tend to make optional a Regular type in STL sense (at least
>> how I understand the concept), where default-constructed state should
>> guarantee no more than moved-from state.

You misunderstand that constraint.  That is a constraint on the user of
the Regular type model, who doesn't know the specific type with which
he's dealing.  There's nothing to prevent the type's author from
guaranteeing more for the default constructed state than for the
moved-from state.  Furthermore, there's no way to make optional<T> a
regular type if T is not also a regular type.  So default constructing T
would not in any way disturb regularity.

I agree that if optional's default ctor called T's default ctor it would not disturb regularity. On the other hand it might (perhaps surprisingly) incur some run-time cost that could have otherwise been avoided. But I admit that if we already accepted that T's default ctro is expensive we wouldn't mind that optional's default ctor is also expensive.

But I claim that it is possible to "regularize" type T with optional if the only thing T is missing, is default ctor.



>> For instance function resize needs to call some constructor, but we
>> need it to be as cheap as possible because next thing that the user
>> will do is to overwrite the current value.  

I disagree strongly with that assertion.  We don't *need* optional<T>{}
to be cheap when T{} is not.  We need optional<T> to be well-behaved
with respect to construction, and especially in the presence of variadic
templates, introducing a non-uniformity like the one we're describing is
most undesirable.  If you want to ensure empty optionals, just use

     v.resize(10, empty_optional)

Fair enough. The words "we need" comes from the fact that I am already thinking in terms of the guarantee that we intended to provide (cheap default ctor) but it is not the essential part of optional.
 

>> It is possible that T's default constructor is also cheap already and
>> creates a value that means "not-a-T" (like std::thread), but in that
>> case you probably need to use type T rather than optional<T>.

Sorry, I don't know what you're getting at here.

Sorry for making too big a mental shortcut. I meant to say the same thing that I replied to Nevin:
https://groups.google.com/a/isocpp.org/d/msg/std-proposals/cXneqUj-5oo/Tm-a5MQ3fMIJ
 
Namely, that if you want to request the call to T's default constructor while creating an optional<T> object (no matter what syntax you use) the decision to use type optional<T> in the first place was probably wrong. This is at least my intuition.

But having said all that, I consider your suggestion (call T's default constructor in optional<T>'s default constructor) a viable option; and if I am the only one in this group to defend the opposite view, then I guess it is an indication that the proposal should be changed.

Thanks,
&rzej

Nevin Liber

unread,
Jun 4, 2012, 1:05:12 AM6/4/12
to std-pr...@isocpp.org
On 3 June 2012 16:23, Andrzej Krzemieński <akrz...@gmail.com> wrote:
Boost Optional as well as this proposal was designed to solve more than one problem. (I do not necessarily say that it is a good thing to do.) The primary goal is to have a compound value that represents either T or nothing (variant<none_t, T>). Another goal is these special guarantees for default constructor.

The debate is whether the constructor that gets those guarantees has to be the default constructor.

Optional, as proposed currently, allows you -- in certain cases -- to make your non-regular type regular.

No, it doesn't.  optional<T> is not a drop in replacement for T, as it requires a different syntax to access the value.  The interface leaks into user code.

Imagine class Date, which stores dates like 12-DEC-2012. It is moveable, copyable assignable, even less-than comparable, but it does not have a default constructor because there is no meaningful default date. The default constructor could create "not-a-date" or a "null-date" , but such choice has its own problems. In such cases optional<Date> creates a fully regular type.

Sure, if the *only* thing your type is missing to make it regular is a default constructor, optional is a way to address that.

To be fair, this does come up occasionally.  The place I've seen it is in map's occasional requirement for default constructibility of its mapped type.

Even more, we guarantee that for global objects its optional's default constructor is initialized during static initialization phase regardless of what T's default-constructor (if provided) guarantees.

If the requirement in my code is loosened to allowing a different type with a different interface than T (which is what optional<T> provides), I have plenty of ways of solving this (some of which might use optional internally).

We allow you to default-initialize fixed-size containers and skip expensive initialization.

Why does this have to be the default constructor, as opposed to some other constructor?
 
The non-uniformity problem could be solved the other way around: disallow any perfect forwarding constructors.

An optional that can never be initialized with a value seems very clunky to me.  I don't want to turn every use of optional into 2-phase initialization.  Delaying construction is not the only use case for optional (if you believe it is, why allow *any* way to unset its value?).
 
Namely, that if you want to request the call to T's default constructor while creating an optional<T> object (no matter what syntax you use) the decision to use type optional<T> in the first place was probably wrong. This is at least my intuition.

Our job is to provide useful libraries, not coding guidelines.  And optional is incredibly useful.  Debate over these details is a healthy thing.
-- 

Jeffrey Yasskin

unread,
Jun 4, 2012, 2:59:31 AM6/4/12
to std-pr...@isocpp.org
FWIW, I think that if optional<T>(a) forwards to T(a), then it would
be pretty bad for optional<T>() not to forward to T(). However, I also
think that optional<T>() should produce the "most empty" instance of
the type, which is the "none" instance, not the T() instance, which
argues for the 'in_place' tag that's in the proposal.

Tags are ugly, but when T is movable, people generally won't need to
use this one, since they can write optional<T>(T(whatever)) or
optional<T>({whatever}). (Sidenote: do compilers have permission to
construct that directly into place, eliding the second
move-construction even if they can't prove it's side-effect-free?
Would it make sense to give them that permission in C++17?) It's only
when people need to put a no-move+no-copy type into their 'optional',
during the 'optional's construction, that they'll need to use the tag.

So +1 to this part of the current proposal.
Jeffrey

Andrzej Krzemieński

unread,
Jun 4, 2012, 3:26:17 AM6/4/12
to std-pr...@isocpp.org

They are allowed to do that (see section 12.8, paragraph 31 in the current standard), of course provided that move constructor is defined and accessible by the caller.
 

Jeffrey Yasskin

unread,
Jun 4, 2012, 3:36:20 AM6/4/12
to std-pr...@isocpp.org
Thanks for looking up the reference. None of the bullets there cover
the move from a {function argument || a temporary that's been bound to
a function argument} into a member variable, so I think compilers
currently aren't allowed to make this optimization unless they prove
it's behavior-preserving (which they'll often be able to do for move
constructors, but anyway).

Jeffrey

Andrzej Krzemieński

unread,
Jun 4, 2012, 3:46:37 AM6/4/12
to std-pr...@isocpp.org


W dniu poniedziałek, 4 czerwca 2012 07:05:12 UTC+2 użytkownik Nevin ":-)" Liber napisał:
On 3 June 2012 16:23, Andrzej Krzemieński <akrz...@gmail.com> wrote:
Boost Optional as well as this proposal was designed to solve more than one problem. (I do not necessarily say that it is a good thing to do.) The primary goal is to have a compound value that represents either T or nothing (variant<none_t, T>). Another goal is these special guarantees for default constructor.

The debate is whether the constructor that gets those guarantees has to be the default constructor.

Optional, as proposed currently, allows you -- in certain cases -- to make your non-regular type regular.

No, it doesn't.  optional<T> is not a drop in replacement for T, as it requires a different syntax to access the value.  The interface leaks into user code.

Sure. What I meant to say is that if you cannot use type Date (from my previous example) with STL containers (or any other library) because of the lack of regularity, you have the option to use a "derivative" type instead: optional<Date>.

Imagine class Date, which stores dates like 12-DEC-2012. It is moveable, copyable assignable, even less-than comparable, but it does not have a default constructor because there is no meaningful default date. The default constructor could create "not-a-date" or a "null-date" , but such choice has its own problems. In such cases optional<Date> creates a fully regular type.

Sure, if the *only* thing your type is missing to make it regular is a default constructor, optional is a way to address that.

To be fair, this does come up occasionally.  The place I've seen it is in map's occasional requirement for default constructibility of its mapped type.

Even more, we guarantee that for global objects its optional's default constructor is initialized during static initialization phase regardless of what T's default-constructor (if provided) guarantees.

If the requirement in my code is loosened to allowing a different type with a different interface than T (which is what optional<T> provides), I have plenty of ways of solving this (some of which might use optional internally).

Again, I do not mean to say that you can "retroactively" turn your type T into optional<T>. But when you design a new thing from scratch optional dives you a convenient way of two-phase initialization, which is occasionally very useful and essential, especially for globals.

You are right that having optional's default constructor do the trivial initialization is not critical for this purpose, but I find it elegant and "safer". Let me give one example of what I mean by "safer". I have a class Toolbox, and I want to implement a "lazy initialization" optimization for one of its members of type Tool:

  class Toolbox
  {
    optional<Tool> tool1;
  };


How it is initialized will be specified in another file (the above is a header file) and I may simply forget to add a proper initialization to tool1. True that in C++11 you can initialize inside class definition:


  class Toolbox
  {
    optional<Tool> tool1 = none;
  };


But I may forget that too, and silently eagerly initialize the tool, or silently trigger dynamic initialization, where I expected static initialization.

We allow you to default-initialize fixed-size containers and skip expensive initialization.

Why does this have to be the default constructor, as opposed to some other constructor?

You are right, this is not really critical, but I find it elegant and intuitive. One example is given above. Also, in case of built-in arrays, you cannot say "use this constructor to initialize every element".

 
The non-uniformity problem could be solved the other way around: disallow any perfect forwarding constructors.

An optional that can never be initialized with a value seems very clunky to me.  I don't want to turn every use of optional into 2-phase initialization.  Delaying construction is not the only use case for optional (if you believe it is, why allow *any* way to unset its value?).

This is by no means what I suggest. I propose to disallow the perfect-forwarding "variadic" constrructor, but leave the constructor from T const&, T&& and  U&& where U is convertible to T.

Ville Voutilainen

unread,
Jun 4, 2012, 3:58:19 AM6/4/12
to std-pr...@isocpp.org
On 4 June 2012 10:46, Andrzej Krzemieński <akrz...@gmail.com> wrote:
> You are right that having optional's default constructor do the trivial
> initialization is not critical for this purpose, but I find it elegant and
> "safer". Let me give one example of what I mean by "safer". I have a class

To draw some (possibly incorrect analogies): empty smart pointers
(unique_ptr, shared_ptr,
the proposed value_ptr) don't default-construct their T when you
construct the pointer.
Neither do empty containers. fstreams don't open files by default. Off
the top of my head,
I can't think of a "smart resource handle" that would create its
handled-T by default.
I guess it's up for debate whether optional is such a handle, but one
could easily think of
it as one.

Ville Voutilainen

unread,
Jun 4, 2012, 4:03:09 AM6/4/12
to std-pr...@isocpp.org
On 4 June 2012 10:58, Ville Voutilainen <ville.vo...@gmail.com> wrote:
> Neither do empty containers. fstreams don't open files by default. Off
> the top of my head,
> I can't think of a "smart resource handle" that would create its
> handled-T by default.

Of course, now that the language has variadic templates and perfect forwarding,
I fully plan to write tons of smart resource handles that _do_ construct their
handled-T, thus getting rid of the remaining uses of new _and_ placement new.
But that's perhaps separate from what optional should do. :)

Andrzej Krzemieński

unread,
Jun 4, 2012, 4:27:55 AM6/4/12
to std-pr...@isocpp.org


W dniu piątek, 1 czerwca 2012 09:57:38 UTC+2 użytkownik Ville Voutilainen napisał:
On 1 June 2012 10:34, Andrzej Krzemieński <akrz...@gmail.com> wrote:
> Hi,
> The link below is the draft of the second revision of the proposal to add
> Standard Library component for representing optional (nullable) objects,
> based on Boost.Optional library.
> http://kojot.sggw.waw.pl/~akrzemi1/optional/tr2.optional.proposal.html
> We would appreciate your feedback.

First of all, thanks! Alisdair mentioned in passing during one of the committee
meetings that we need "vocabulary types", optional being one of them.

I have to admit that having "none" directly under std gives me the creeps. :)
Is it possible to nest it under optional, somehow? I think I can get over it,
though...

Would name nothing (of type nothing_t) be a more acceptable alternative? The argument for not getting it under optional namespace is that it could be reused by other libraries. like Variant. It could also be used as an improved void type.

Ville Voutilainen

unread,
Jun 4, 2012, 4:58:01 AM6/4/12
to std-pr...@isocpp.org
On 4 June 2012 11:27, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>> I have to admit that having "none" directly under std gives me the creeps.
> Would name nothing (of type nothing_t) be a more acceptable alternative? The
> argument for not getting it under optional namespace is that it could be
> reused by other libraries. like Variant. It could also be used as an
> improved void type.

I don't mind the name. And indeed we probably _want_ to use it with
other facilities as well. The question becomes, are we mentally ready to
allow vector<int> x(none), because that seems like a probable consequence?

The rationale for none is, as I said before, sound. Perhaps it's possible to
extract it to an individual proposal with its rationale, and try and make it
more palatable that way.

Alberto Ganesh Barbati

unread,
Jun 4, 2012, 5:16:06 AM6/4/12
to std-pr...@isocpp.org

Il giorno 01/giu/2012, alle ore 12:03, Andrzej Krzemieński ha scritto:

>
> W dniu piątek, 1 czerwca 2012 11:47:53 UTC+2 użytkownik Alberto Ganesh Barbati napisał:
>
>> By the way, given that all standard containers use the name "emplace", wouldn't be nicer to stay consistent and use "emplace_t", given that the semantic is similar?
>
> The two -- in_place_t and in_place -- constitute a "pair" the first is a type used in optional interface declarations, the other will be used by the users. If I rename one, I guess I also have to rename the other, but name emplace is already taken (and has different type).

"emplace" is currently defined as a member function of a few container types, but std::emplace is not taken and could be used. It would make it easier for the casual reader to understand that:

std::optional<A> o(std::emplace, arg);

and

std::optional<A> o;
o.emplace(arg);

achieve the same effect. However...

> But now I think I like Chris's suggestion even better: in the unusual case just initialize in two lines. The only other place where such emplacement constructor would be useful is for temporaries in return statements or complex expressions, but in those cases perhaps an improved variant of make_optional could do.

... I definitely agree with this idea, which is cleaner and better.

Ganesh

Andrzej Krzemieński

unread,
Jun 4, 2012, 5:33:58 AM6/4/12
to std-pr...@isocpp.org


W dniu poniedziałek, 4 czerwca 2012 10:58:01 UTC+2 użytkownik Ville Voutilainen napisał:
On 4 June 2012 11:27, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>> I have to admit that having "none" directly under std gives me the creeps.
> Would name nothing (of type nothing_t) be a more acceptable alternative? The
> argument for not getting it under optional namespace is that it could be
> reused by other libraries. like Variant. It could also be used as an
> improved void type.

I don't mind the name. And indeed we probably _want_ to use it with
other facilities as well. The question becomes, are we mentally ready to
allow vector<int> x(none), because that seems like a probable consequence?

Perhaps I am diverging off topic, but what would

  vector<int> x(none);

mean? Explicit statement that we are interested in empty vector?


The rationale for none is, as I said before, sound. Perhaps it's possible to
extract it to an individual proposal with its rationale, and try and make it
more palatable that way.

Ville Voutilainen

unread,
Jun 4, 2012, 6:13:57 AM6/4/12
to std-pr...@isocpp.org
On 4 June 2012 12:33, Andrzej Krzemieński <akrz...@gmail.com> wrote:
> Perhaps I am diverging off topic, but what would
>   vector<int> x(none);
> mean? Explicit statement that we are interested in empty vector?

Yes.

To get back on topic, nullptr doesn't cut it, so we need something
else. And that
something else is apparently useful for optional and variant. Whether we want
to use that something-else for other things remains to be seen.

I have no usage experience on optional/variant, so I don't know whether
a clear() member function would be a suitable alternative for an assignment
that disengages an optional. Or reset(), if you want to be similar to
unique_ptr/auto_ptr.

Dave Abrahams

unread,
Jun 4, 2012, 11:28:00 AM6/4/12
to std-pr...@isocpp.org

on Sun Jun 03 2012, Marc Glisse <marc.glisse-AT-gmail.com> wrote:

> I was actually surprised to see a proposal for optional before/
> separately from one for variant. As far as I understand, optional is
> a special case of variant with a few interfaces added for
> convenience. I really think we want variant and optional in the
> standard, and their tight relation makes it hard to judge them
> separately. I do hope this sounds more like an encouragement for a
> variant proposal than discouragement for the optional one.

+1, and well said.

Dave Abrahams

unread,
Jun 4, 2012, 11:42:51 AM6/4/12
to std-pr...@isocpp.org

on Sun Jun 03 2012, Andrzej Krzemieński <akrzemi1-AT-gmail.com> wrote:

> I feel uncomfortable removing cheap default constructor just yet --
> although I find the non-uniformity argument important. Let me try to
> defend it.

OK

> Optional, as proposed currently, allows you -- in certain cases -- to
> make your non-regular type regular. Imagine class Date, which stores
> dates like 12-DEC-2012. It is moveable, copyable assignable, even
> less-than comparable, but it does not have a default constructor
> because there is no meaningful default date. The default constructor
> could create "not-a-date" or a "null-date" , but such choice has its
> own problems. In such cases optional<Date> creates a fully regular
> type.

Yes.

> Or more generally: We guarantee that that optional<T> is
> default-constructible even if T is not.

OK, but *that*, I promise you, was not a design goal of optional... nor
do I think "adding a missing default constructor" is optional's proper
purpose.

> Even more, we guarantee that for global objects its optional's default
> constructor is initialized during static initialization phase
> regardless of what T's default-constructor (if provided) guarantees.

Well, that's a little silly, IMO. You can always use a T* if you
need a statically-initialized object that refers to a T.

> We allow you to default-initialize fixed-size containers and skip
> expensive initialization.

Yeah... so as I said, simply don't use default initialization.

> My previous example was blindly yanked from the proposal and made
> little sense so let me try again:
>
> // the problem:
> ExpensiveDefaultCtor array[100]; // 100 expensive default constructions
> std::array<ExpensiveDefaultCtor, 100> arr; // 100 default constructions
> std::vector<ExpensiveDefaultCtor> vec(100); // 100 default constructions
>
> // the solution:
> optional<ExpensiveDefaultCtor> array[100]; // 100 cheap default constructions
> array<optional<ExpensiveDefaultCtor>, 100> arr;
> vector<optional<ExpensiveDefaultCtor>> vec(100);
>
> There is also some psychological argument. I am not sure if you feel
> the same way. If you read the following lines:
>
> optional<Model> m;
> // ...
> m = Model(params);
>
> Would you have expected that it actually initialized the Model twice?

This argument actually holds some weight with me, but it's quite
possible my default interpretation would be easily overcome.

> Especially for globals, I would think it is common to write just:
>
> optional<Model> m;
>
> Which says "for now, initialize statically to 'not-a-model',I will
> initialize properly in main when I have read the config file."

optional<Model> m = none;

is not bad, and might be worth the gain in uniformity.

> The non-uniformity problem could be solved the other way around:
> disallow any perfect forwarding constructors. To me, they do not
> appear that essential. You can always use solutions that require an
> extra move constructor:
>
> opional<Container> compute()
> {
> if (...) {
> return Container{};
> }
> else {
> return none;
> }
> }
>
> The rare cases where type T is not moveable, are rare enough to be
> handled by more clumsy "in_place" constructor.

You might be convincing me, here. I'd like to see more specifically
what you mean by "disallow any perfect forwarding constructors" and what
its implications would be for users.

> >> Also, we tend to make optional a Regular type in STL sense (at least
> >> how I understand the concept), where default-constructed state should
> >> guarantee no more than moved-from state.
>
> You misunderstand that constraint. That is a constraint on the
> user of the Regular type model, who doesn't know the specific type
> with which he's dealing. There's nothing to prevent the type's
> author from guaranteeing more for the default constructed state
> than for the moved-from state. Furthermore, there's no way to
> make optional <T> a regular type if T is not also a regular type.
> So default constructing T would not in any way disturb regularity.
>
> I agree that if optional's default ctor called T's default ctor it
> would not disturb regularity. On the other hand it might (perhaps
> surprisingly) incur some run-time cost that could have otherwise been
> avoided. But I admit that if we already accepted that T's default
> ctro is expensive we wouldn't mind that optional's default ctor is
> also expensive.
>
> But I claim that it is possible to "regularize" type T with optional
> if the only thing T is missing, is default ctor.

Yes, that's correct. I was too broad in my claim.

> >> It is possible that T's default constructor is also cheap already and
> >> creates a value that means "not-a-T" (like std::thread), but in that
> >> case you probably need to use type T rather than optional<T>.
>
> Sorry, I don't know what you're getting at here.
>
> Sorry for making too big a mental shortcut. I meant to say the same
> thing that I replied to Nevin:
> https://groups.google.com/a/isocpp.org/d/msg/std-proposals/
> cXneqUj-5oo/Tm-a5MQ3fMIJ
>
> Namely, that if you want to request the call to T's default
> constructor while creating an optional<T> object (no matter what
> syntax you use) the decision to use type optional<T> in the first
> place was probably wrong. This is at least my intuition.

Interesting idea.

> But having said all that, I consider your suggestion (call T's
> default constructor in optional<T>'s default constructor) a viable
> option; and if I am the only one in this group to defend the opposite
> view, then I guess it is an indication that the proposal should be
> changed.

Let's not hurry; I think there's still more discussion to be had ;-)

Fernando Cacciola

unread,
Jun 4, 2012, 1:11:49 PM6/4/12
to std-pr...@isocpp.org
Hi Andrzej,


  vector<int> v1 = {1, 2, 3}; // 3-element vector
  vector<int> v2 = {}; // 0-element vector
  optional<vector<int>> o1 = {1, 2, 3}; // 3-element vector
  optional<vector<int>> o2 = {}; // disengaged optional

 
Interestingly,  I would have expected the case of an empty initialization list to differ from default-construction. That is, I would have liked that it called the initializer-list-constructor with an empty list instead.
Unfortunately (to me), 8.5.4/3 specifically states otherwise.

Dave's argument for uniformity is quite strong IMO, specially if we consider the case where there is an empty list and not just:

optional<vector<int>> o2 ;


I'm pretty sure I can claim that in the great majority of the cases where optional<> is used, the object is first introduced in disengaged state, as in:

optional<T> o ;

o = ...

However, I don't think that such broad use case necessarily means that a default-constructed optional *should* default to disengaged, for I can just as easily type:

optional<T> o = none ;

o = ...

Likewise, bypassing expensive construction of Ts, specially many of them, is certainly important, but I don't think the choice of which optional constructor bypasses it (the default or one specialized for this) affects the feature. You can just do:

vector<optional<X>> ov(10000,none);

and achieve the same effect.

So, it seems to me that this debate is ultimately about end-user convenience rather than features supported.

In that regard, it just happens that I also co-authored a type which is quite similar to optional<>, but serves a slightly different purpose (so it is a different type). That is the type Uncertain<T>, in the CGAL library

FWIW,

Uncertain<T> u ;

introduces an object with *determined* certainty, and forwards--and is equivalent-- to default construction of T
While if you want the indeterminate state instead, you have to write:

Uncertain<T> u = Uncertain<T>::indeterminate();

Now, just as in the case of optional<>, it is almost always the case that you start off with an indeterminate object (for the same reason you start off with a disengaged optional), which means that you almost never use the default constructor and type the other more verbose version instead.

My point with this is that I use that Uncertain<> type *a lot* and never felt that I wished I could have just used the default-constructor instead (say to save keystrokes). In fact, I consider the explicitness of using  Uncertain<T>::indeterminate() a feature of the source code.

So, while I know this is an important deviation from Boost.Optional, I'm inclined to follow Dave's suggestion here.

Best

--
Fernando Cacciola
SciSoft Consulting, Founder
http://www.scisoft-consulting.com 









Andrzej Krzemieński

unread,
Jun 4, 2012, 3:39:34 PM6/4/12
to std-pr...@isocpp.org
 A couple of people here have expectation (which I did not share until now) that if a given initialization for object of of type T works it should also work (compile) for optional<T>, not to mention that the behaviour should also be the same:

  T x{12kg, 3m, "container"};
  optional<T> y{12kg, 3m, "container"};

I.e., since the initialization of x worked, the initialization of y should also work and x == *y. This requires an ideally forwarding variadic constructor that would probably be declared as:

  template <class... U> optional(U&&... u);

(Of course, there is a limit to perfect forwarding. For instance, it does not work for arguments of type initializer_list<R>). What I mean to say is that such perfect forwarding constructor is not critical for optional. Similarly, the initializer_list constructor is not essential. Users can always use the constructor from T&& with only tiny inconvenience:

  optional<vector<int>> ov{{1, 2, 3}};
  optional<T> ot{{12kg, 3m, "container"}};

The only exception worth making would be to allow the "forwarding of converting constructor". I.e., (only) if this works:

  std::string s = "text";

This should also work:

  optional<std::string> s = "text";

In addition to these, we could also provide the explicit "in_place" constructor. I believe such set of constructors is consistent and does not suffer from the non-uniformity problem, if we eliminate the initializer_list ctor. But I do not necessarily insist on it. I am getting more and more convinced about the "forwarding default constructor" idea.

I can think of the three typical (in my experience usages) of optional:

(1) When I need to return either a valid T, or nothing:

  optional<T> fun()
  {
    if (...) return T(1, 2, 3);
    else return none;
  }

In such a case I would never even think about using default constructor (that creates a disengaged optional) as it would obscure my intentions. (So, I do not mind forwarding default ctor here)

(2) "Optionalizing" function parameter: I may need to change my function's signature from fun(T) to fun(optional<T>) and expect that every call like
fun(cat)
will also work after the change. In this case perfect-forwarding variadic constructor (and forwarding default ctor) definately win).

(3) A two-phase initialization:


  optional<Model> m;
 
// ...

  m.emplace(param1, param2);

 
In such case, I feel, the forwarding default ctor adds confusion. On the other hand, I am not sure how realistic such confusion is. I.e., is it possible that Model requires a two=phase init, has a constructor that requires parameters, and at the same time provides a default constructor?  But it might be real difference for types with trivial constructors where we would be risking having a rubbish initial value, but I guess we could force zero initialization here.

I consider the following example irrealistic though:

  void fun()
  {
    optional<Model> m{param1, param2};
    // ...
  }

If you know you want to initialize the contained value, why not just use non-optional Model?

I guess I am not addressing any of your questions. I am just thinking aloud.

Regards,
&rzej

Andrzej Krzemieński

unread,
Jun 4, 2012, 4:14:58 PM6/4/12
to std-pr...@isocpp.org

Hi Ferando,
I understand the reasoning. I do not really find "regularizing a non-regular type" essential use case for optional, or even staying close to Boost.Optional. But I feel the choice to have default ctor do the "disengaged" state offered some level of protection against certain programming errors caused by omission. I am trying to imagine how an implementation of a function reading next char from a stream would look like:

optional<char> readChar( Stream str )
{
  optional<char> ans;

  if (str.buffered()) {
    ans = str.readBuffered();
    clearBuffer();
  }
  else if (!str.end()) {
    ans = str.readChar();
  }

  return ans;
}

I create to-be-returned variable with default constructor because I do not care about its value just yet. I only need a name to be used in the (sole) return statement.I could have initialized it with none, to be sure, but I forgot, or I was convinced that optional would work like STL containers: by default it is empty and then I may fill it with value(s) if I find any. Now, if optional's default constructor is "forwarding" my function will do the wrong thing. One could argue that I should have written the documentation or be more careful, but on the other hand, I guess the high quality of the software is also realized in how it prevents inadvertent mistakes and how intuitive it is. If it is only my intuition that works this way, then it is just a question of personal preference. But is it not just natural to expect that optional's default ctor will create a disengaged object (especially if someone uses the zero-or-one-element container model).

Of course, this is only one argument. I agree the uniformity of forwarding is essential. Perhaps I will have to live with this default ctor gotcha. Another way to avoid either problem is not to allow the forwarding constructors at all and just require that optional<T> is initialized with T.

Regards,
&rzej

Nevin Liber

unread,
Jun 4, 2012, 4:24:35 PM6/4/12
to std-pr...@isocpp.org
On 4 June 2012 15:14, Andrzej Krzemieński <akrz...@gmail.com> wrote:
 But I feel the choice to have default ctor do the "disengaged" state offered some level of protection against certain programming errors caused by omission.

If that is really what you are trying to stop, the best way (for some definition of best) is to not allow any default constructor (as someone is bound to get it wrong), and to explicitly specify whether or not it is engaged, as in:

struct engage_t {};
struct disengage_t {};

class optional {
public:
    explicit optional(disengage_t) { /* ... */ }

    template<typename... Args>
    explicit optional(engage_t, Args&&...)
    { /* ... */ }

    // ...
};

Note:  I'm not necessarily recommending this, just presenting this as a possibility...

Nevin Liber

unread,
Jun 4, 2012, 4:53:28 PM6/4/12
to std-pr...@isocpp.org
On 4 June 2012 14:39, Andrzej Krzemieński <akrz...@gmail.com> wrote:



  void fun()
  {
    optional<Model> m{param1, param2};
    // ...
  }

If you know you want to initialize the contained value, why not just use non-optional Model?

Because I may need to destroy and create a new one later or shorten its lifetime.

Suppose you have an object representing an active database connection.  If the connection goes down, you might need to establish a new one.  Now, such an object is typically noncopyable, so two options under C++03 for this functionality are to add an intrusive reset method that does the same thing as assignment typically would, or use optional to hold it.  (Now, in C++11, making the object moveable but noncopyable is a third option.)

vb.ma...@gmail.com

unread,
Jun 4, 2012, 6:15:21 PM6/4/12
to std-pr...@isocpp.org
On Monday, June 4, 2012 7:23:41 AM UTC+10, Andrzej Krzemieński wrote:
 
Optional, as proposed currently, allows you -- in certain cases -- to make your non-regular type regular. Imagine class Date, which stores dates like 12-DEC-2012. It is moveable, copyable assignable, even less-than comparable, but it does not have a default constructor because there is no meaningful default date. The default constructor could create "not-a-date" or a "null-date" , but such choice has its own problems. In such cases optional<Date> creates a fully regular type.

My first reaction was that that might be a useful addition/modification of that mentioned Date class API. On the second thought though I feel that Date provided certain interface for a reason and my expectation would be that optional (as a "convenience"/wrapper class) objective would be to honor that API rather than try "improving" it. I think as a user I'd much prefer optional<T> to be behave as close to T as possible (within reasonable restrictions). That'd not just make deployment of optional more intuitive but easier for the existing code.

Or more generally: We guarantee that that optional<T> is default-constructible even if T is not. Even more, we guarantee that for global objects its optional's default constructor is initialized during static initialization phase regardless of what T's default-constructor (if provided) guarantees.

Again, if T is not default-constructible, then more likely than not there was a legitimate design reason for that. I am not sure it's optional's purpose trying to "improve" other-class interface.
 
We allow you to default-initialize fixed-size containers and skip expensive initialization. My previous example was blindly yanked from the proposal and made little sense so let me try again:

  // the problem:
  ExpensiveDefaultCtor array[100]; // 100 expensive default constructions
  std::array<ExpensiveDefaultCtor, 100> arr; // 100 default constructions 
  std::vector<ExpensiveDefaultCtor> vec(100); // 100 default constructions

  // the solution:
  optional<ExpensiveDefaultCtor> array[100]; // 100 cheap default constructions
  array<optional<ExpensiveDefaultCtor>, 100> arr;
  vector<optional<ExpensiveDefaultCtor>> vec(100);

It appears to be a noble goal. However, the "problem" :-) of that solution IMO is that it tries to "fix" the behavior of the underlying class which are perceived broken. The "100 expensive default constructions" lines might well be expensive but it's there due to a design choice or due to sloppy programming. I do not immediately agree that it's optional's business trying to improve somebody else's design or to "fix"/encourage incompetent coding. To me the "optional" name implies well-defined purpose of providing optional no-value state... and I might add with as little interference (interface/behavior modification) as achievable. I feel trying to use it as a broken-class fixer is mis-guided.

There is also some psychological argument. I am not sure if you feel the same way. If you read the following lines:

  optional<Model> m;
  // ...
  m = Model(params);

Would you have expected that it actually initialized the Model twice?

I'd say it depends on what one grew up with and what our expectations are. If we want to see "optional<Model> m" behaving as a built-in, then I'd expect the line to be cheap. Howeve, I personally, do not want to see such a line as it's IMO against C++ spirit. In C++ (as I understand) we strive for "declaration is initialization" as we know that "Foo foo;" *is* initialized. Having said that I do agree that the line might be confusing. So, my preference would be to be explicit


optional<Model> m = none;
// ...
m = Model(params);
 
Especially for globals, I would think it is common to write just:

  optional<Model> m;
Which says "for now, initialize statically to 'not-a-model',I will initialize properly in main when I have read the config file."

I have to disagree. I personally do not see "initialize ... to". I see "initialize with the default cnstr". If I want "initialize to", I'd expect


optional<Model> m = none;
 
The non-uniformity problem could be solved the other way around: disallow any perfect forwarding constructors. To me, they do not appear that essential.

Hmm, that's something I have to strongly disagree with. Say, as a user I've got quite a bit of code using T. Now during refactoring (requirements change) I realize I need optional<T>. I'd very much like to deploy optional<T> (replace many places where T is used with optional<T>) with as little effort as possible. So, perfect forwarding and optional honoring the underlying-class behavior/API would be or real help.

...

I agree that if optional's default ctor called T's default ctor it would not disturb regularity.

And that IMO is a serious benefit design-wise and deployment-wise.
 
On the other hand it might (perhaps surprisingly) incur some run-time cost that could have otherwise been avoided.

Again, I do not think that's optional's business trying to "improve"/modify underlying API.

 
But I admit that if we already accepted that T's default ctro is expensive we wouldn't mind that optional's default ctor is also expensive.

Yes

 
But I claim that it is possible to "regularize" type T with optional if the only thing T is missing, is default ctor.

The "missing" word implies flawed T that optional is trying to fix. I am not sure I can agree with that. To me optional should treat T's API as a gospel and add "optionality" as its name suggests. Nothing more.

All obviously just IMHO.

Best,
V.

Andrzej Krzemieński

unread,
Jun 5, 2012, 2:44:29 AM6/5/12
to std-pr...@isocpp.org

This is true and I incorrectly implied that such usage is necessarily incorrect. I am not sure about the database example (if connection tends to break, the DB class designer has to provide a tool for handling that anyway) but I agree that you may need to end the lifetime of the object prematurely. However, I consider such usage _rare_. When you decide to use optional like this, you are probably an experienced programmer, you gave some thought into this solution, rejected alternatives... In such situation, I guess, you wouldn't mind a somewhat more verbose syntax:

  optional<Model> m{in_place, param1, param2};

And accept the fact that you need to type some more, in order that basic use cases are simple and safe. 

Andrzej Krzemieński

unread,
Jun 5, 2012, 2:58:23 AM6/5/12
to std-pr...@isocpp.org


W dniu poniedziałek, 4 czerwca 2012 17:42:51 UTC+2 użytkownik Dave Abrahams napisał:
> There is also some psychological argument. I am not sure if you feel
> the same way. If you read the following lines:
>
>   optional<Model> m;
>   // ...
>   m = Model(params);
>
> Would you have expected that it actually initialized the Model twice?

This argument actually holds some weight with me, but it's quite
possible my default interpretation would be easily overcome.

Now I believe I can express my intuition more objectively. One way of looking at optional is a variable-size container with the maximum size of 1 element. Variable size containers when default-initialized are empty. If you hold that expectation, you will get confused.
 
> Especially for globals, I would think it is common to write just:
>
>   optional<Model> m;
>
> Which says "for now, initialize statically to 'not-a-model',I will
> initialize properly in main when I have read the config file."

   optional<Model> m = none;

is not bad, and might be worth the gain in uniformity.

Again, I would be willing to accept that as a compromise.
 

Andrzej Krzemieński

unread,
Jun 5, 2012, 3:23:49 AM6/5/12
to std-pr...@isocpp.org

This interface is very elegant. On the other hand I also consider just having a default constructor a valid goal in itself. This is to make optional<T> a Regular type, at least for Regular T's; so that there are no surprises when using it with STL components. I tend to focus on the least surprising behaviour of the default constructor.

Also note that apart form the controversial default constructor the above closely corresponds to the current proposal id you replace disengage_t with none_t and engage_t with in_place_t.
 

Andrzej Krzemieński

unread,
Jun 5, 2012, 3:59:40 AM6/5/12
to std-pr...@isocpp.org


W dniu wtorek, 5 czerwca 2012 00:15:21 UTC+2 użytkownik (Nieznane) napisał:
On Monday, June 4, 2012 7:23:41 AM UTC+10, Andrzej Krzemieński wrote:
 
Optional, as proposed currently, allows you -- in certain cases -- to make your non-regular type regular. Imagine class Date, which stores dates like 12-DEC-2012. It is moveable, copyable assignable, even less-than comparable, but it does not have a default constructor because there is no meaningful default date. The default constructor could create "not-a-date" or a "null-date" , but such choice has its own problems. In such cases optional<Date> creates a fully regular type.

My first reaction was that that might be a useful addition/modification of that mentioned Date class API. On the second thought though I feel that Date provided certain interface for a reason and my expectation would be that optional (as a "convenience"/wrapper class) objective would be to honor that API rather than try "improving" it. I think as a user I'd much prefer optional<T> to be behave as close to T as possible (within reasonable restrictions). That'd not just make deployment of optional more intuitive but easier for the existing code.

I think I disagree with the argument that "we should not fix existing APIs". I explain my reasoning below.
 
Or more generally: We guarantee that that optional<T> is default-constructible even if T is not. Even more, we guarantee that for global objects its optional's default constructor is initialized during static initialization phase regardless of what T's default-constructor (if provided) guarantees.

Again, if T is not default-constructible, then more likely than not there was a legitimate design reason for that. I am not sure it's optional's purpose trying to "improve" other-class interface.
 
We allow you to default-initialize fixed-size containers and skip expensive initialization. My previous example was blindly yanked from the proposal and made little sense so let me try again:

  // the problem:
  ExpensiveDefaultCtor array[100]; // 100 expensive default constructions
  std::array<ExpensiveDefaultCtor, 100> arr; // 100 default constructions 
  std::vector<ExpensiveDefaultCtor> vec(100); // 100 default constructions

  // the solution:
  optional<ExpensiveDefaultCtor> array[100]; // 100 cheap default constructions
  array<optional<ExpensiveDefaultCtor>, 100> arr;
  vector<optional<ExpensiveDefaultCtor>> vec(100);

It appears to be a noble goal. However, the "problem" :-) of that solution IMO is that it tries to "fix" the behavior of the underlying class which are perceived broken. The "100 expensive default constructions" lines might well be expensive but it's there due to a design choice or due to sloppy programming. I do not immediately agree that it's optional's business trying to improve somebody else's design or to "fix"/encourage incompetent coding. To me the "optional" name implies well-defined purpose of providing optional no-value state... and I might add with as little interference (interface/behavior modification) as achievable. I feel trying to use it as a broken-class fixer is mis-guided.

I leave the argument "don't try to fix a broken class" for the end. But I agree that skipping expensive default construction does not need to be handled by optional, and even if it is handled by optional it does not have to be its default ctor. This minor goal of optional can be safely compromised.

There is also some psychological argument. I am not sure if you feel the same way. If you read the following lines:

  optional<Model> m;
  // ...
  m = Model(params);

Would you have expected that it actually initialized the Model twice?

I'd say it depends on what one grew up with and what our expectations are. If we want to see "optional<Model> m" behaving as a built-in, then I'd expect the line to be cheap. Howeve, I personally, do not want to see such a line as it's IMO against C++ spirit. In C++ (as I understand) we strive for "declaration is initialization" as we know that "Foo foo;" *is* initialized. Having said that I do agree that the line might be confusing. So, my preference would be to be explicit

My reasoning here is that first, we do want optional to have a default constructor (so as to make it a Regular type). You can argue that it is a valid goal in itself. But since it is (at least for now) people will use it, sometimes inadvertently, because it is so short and attractive to write. Since they will use it, it should provide the least surprising behavior. I argue that default-initializing optional to disengaged state is the most natural and is not only the question of personal preference. One of the ways that Boost.Optional was taught is that it is a special case of STL container that has the maximum size of 1. Containers are default-initialized to the state where they are empty, which corresponds to the disengaged optional state. If we are to abandon default-initialize-to-disengaged-state behaviour we would have to make it explicit that you cannot think of optional as a special container, and also that you cannot think of optional as variant<none_t, T> (because it is also default-initialized to storing none_t).


optional<Model> m = none;
// ...
m = Model(params);
 
Especially for globals, I would think it is common to write just:
  optional<Model> m;
Which says "for now, initialize statically to 'not-a-model',I will initialize properly in main when I have read the config file."

I have to disagree. I personally do not see "initialize ... to". I see "initialize with the default cnstr". If I want "initialize to", I'd expect

optional<Model> m = none;

I agree that I can write my intention down more explicitly. But you will also agree with mee that if the default constructor notation got there inadvertently it will cause a confusion and likely a programming error.

The non-uniformity problem could be solved the other way around: disallow any perfect forwarding constructors. To me, they do not appear that essential.

Hmm, that's something I have to strongly disagree with. Say, as a user I've got quite a bit of code using T. Now during refactoring (requirements change) I realize I need optional<T>. I'd very much like to deploy optional<T> (replace many places where T is used with optional<T>) with as little effort as possible. So, perfect forwarding and optional honoring the underlying-class behavior/API would be or real help.

This is a valid and a strong argument. If it is universally agreed that it is stronger than the potential confusion with default ctor, we will go with the perfect-forwarding constructor, I guess. For now I would still like to explore the problem and the options. 

Perhaps the inadvertent usage of "forwarding default ctor" would simply not compile for cases like:

  optional<Model> m;

because Model is likely not to provide the default ctor itself.

...
I agree that if optional's default ctor called T's default ctor it would not disturb regularity.

And that IMO is a serious benefit design-wise and deployment-wise.
 
On the other hand it might (perhaps surprisingly) incur some run-time cost that could have otherwise been avoided.

Again, I do not think that's optional's business trying to "improve"/modify underlying API.

 
But I admit that if we already accepted that T's default ctro is expensive we wouldn't mind that optional's default ctor is also expensive.

Yes

 
But I claim that it is possible to "regularize" type T with optional if the only thing T is missing, is default ctor.

The "missing" word implies flawed T that optional is trying to fix. I am not sure I can agree with that. To me optional should treat T's API as a gospel and add "optionality" as its name suggests. Nothing more.
 
I tend to disagree with this reasoning. It is not really about "fixing" what I consider "broken". It is more about having a tool for easily tailoring the API to suit your usage requirements. Consider the example of type Date. I find it very interesting and practical. Its designer already faced a number of trade-offs that he is likely dissatisfied with. Most notably he removed the default constructor. It is beneficial because there is no risk of creating an "indeterminate date" or "not-a-date". But on the other hand it poses a number of problems. The type is not regular; you will face a number of problems when you try to use it with STL. You cannot easily say that "no date was computed". So this is what the author of Date could have said: since I have to make a call, I choose to remove the default ctor, because if you really need it, you can easily adapt the type yourself (by wrapping it into optional or some such). In fact it could have been the author's intention that you modify his type like that. This is IMO better than the choice that was made for Boost's date types, where you have to specify a flag while compiling the date that controls whether the date class does or does not provide a default ctor (that creates not-a-date).

But this is only one argument that I disagree with. I sympathize with the desire to have a perfect-forwarding constructor, and the uniformity. It is just that I also see the problems that it would cause.

Vladimir Batov

unread,
Jun 5, 2012, 6:54:56 AM6/5/12
to std-pr...@isocpp.org
> On Tuesday, June 5, 2012 5:59:40 PM UTC+10, Andrzej Krzemieński wrote:
...
My reasoning here is that first, we do want optional to have a default constructor (so as to make it a Regular type). You can argue that it is a valid goal in itself. But since it is (at least for now) people will use it, sometimes inadvertently, because it is so short and attractive to write. Since they will use it, it should provide the least surprising behavior. I argue that default-initializing optional to disengaged state is the most natural and is not only the question of personal preference.

I understand your reasoning and preference. I really do. I concur that there have been a number of classes following that pattern. I find it unfortunate as I found quite helpful being explicit while writing code. Say, if I am staring at some unfamiliar code, then "Foo foo(none);" tells me considerably more than "Foo foo;". The latter requires more knowledge at your fingertips to read code confidently and efficiently. One might argue that everyone needs to know std and boost classes and their behavior. Agreed. My problem though is that those classes set a trend that people start mimicking in their code. Just my view.
 
One of the ways that Boost.Optional was taught is that it is a special case of STL container that has the maximum size of 1. Containers are default-initialized to the state where they are empty, which corresponds to the disengaged optional state. If we are to abandon default-initialize-to-disengaged-state behaviour we would have to make it explicit that you cannot think of optional as a special container, and also that you cannot think of optional as variant<none_t, T> (because it is also default-initialized to storing none_t).

Yes, if optional to be treated as a kind of container, then I have to agree... However, when I think of optional, I see it more like a proxy as it serves as a gateway to access underlying-class API which containers do not provide. Still, one might argue that shared_ptr might be an example of an optional proxy and it has the def. cnstr as you advocate. Indeed, the difference though is that shared_ptr does not try to provide perfect forwarding. If optional chooses not to provide perfect forwarding then I fully agree with your arguments. If optional is to provide perfect forwarding, then it has to be "perfect" :-), i.e. the def. cnstr of the underlying class needs to be "forwardable" as well.
 

optional<Model> m = none;
// ...
m = Model(params);
 
Especially for globals, I would think it is common to write just:
  optional<Model> m;
Which says "for now, initialize statically to 'not-a-model',I will initialize properly in main when I have read the config file."

I have to disagree. I personally do not see "initialize ... to". I see "initialize with the default cnstr". If I want "initialize to", I'd expect

optional<Model> m = none;

I agree that I can write my intention down more explicitly. But you will also agree with mee that if the default constructor notation got there inadvertently it will cause a confusion and likely a programming error.

I agree with your statement *if* optional is to follow the beaten path or deployment pattern (like shared_ptr) and not to provide perfect forwarding. Perfect forwarding is a new "toy" and requires our expectations retrained. Perfect forwarding features tighter association between the underlying class and its proxy. So, once people get comfortable that
"optional<Foo> foo(1,2,3);" calls underlying Foo::Foo(1,2,3), then they'll equally (IMO) expect that "optional<Foo> foo;" calls Foo::Foo().
 
...

Perhaps the inadvertent usage of "forwarding default ctor" would simply not compile for cases like:

  optional<Model> m;

because Model is likely not to provide the default ctor itself.

And to me personally, it's important that optional honors Model API, i.e. it does not hijack the def. cnstr if it exists and it does not create the def. cnstr if it does not in the underlying class.

 
...

But I claim that it is possible to "regularize" type T with optional if the only thing T is missing, is default ctor.

The "missing" word implies flawed T that optional is trying to fix. I am not sure I can agree with that. To me optional should treat T's API as a gospel and add "optionality" as its name suggests. Nothing more.
 
I tend to disagree with this reasoning. It is not really about "fixing" what I consider "broken". It is more about having a tool for easily tailoring the API to suit your usage requirements. Consider the example of type Date. I find it very interesting and practical. Its designer already faced a number of trade-offs that he is likely dissatisfied with. Most notably he removed the default constructor. It is beneficial because there is no risk of creating an "indeterminate date" or "not-a-date".

Agreed.
 
But on the other hand it poses a number of problems. The type is not regular; you will face a number of problems when you try to use it with STL.

Could you please elaborate what kind of problems that might be. I do not believe STL requires types to be regular.

You cannot easily say that "no date was computed". So this is what the author of Date could have said: since I have to make a call, I choose to remove the default ctor, because if you really need it, you can easily adapt the type yourself (by wrapping it into optional or some such). In fact it could have been the author's intention that you modify his type like that.

I agree with all the above. Our only difference is that your preference for "no date was computed" is "optional<Date> date;" and mine is "optional<Date> date(none);". The latter is self-documented. The former requires the reader to know the def.cnstr behavior, i.e. an additional entry-level requirement. Otherwise, from efficiency point of view it is a disaster as a) one shifts his attention to the relevant book and loses his focus or b) maintains his focus but only guesses what the def. cnstr might do.

 
This is IMO better than the choice that was made for Boost's date types, where you have to specify a flag while compiling the date that controls whether the date class does or does not provide a default ctor (that creates not-a-date).

Agreed.
 
But this is only one argument that I disagree with. I sympathize with the desire to have a perfect-forwarding constructor, and the uniformity. It is just that I also see the problems that it would cause.
 
 Sure. Ultimately it's your decision and your perception of good and bad. And I do realize that trying to change one's convictions (especially strong ones like with regard to the def. cnstr) with a few emails is futile. Still, I dare to say that the perception of "problems that it would cause" and the urge to squeeze every class into the Procrustean bed of "regular" are somewhat exaggerated.

Best,
V.

Ville Voutilainen

unread,
Jun 5, 2012, 7:06:02 AM6/5/12
to std-pr...@isocpp.org
On 5 June 2012 13:54, Vladimir Batov <vb.ma...@gmail.com> wrote:
> fully agree with your arguments. If optional is to provide perfect
> forwarding, then it has to be "perfect" :-), i.e. the def. cnstr of the
> underlying class needs to be "forwardable" as well.

That's a good point. To re-emphasize, if optional is to forward anything,
it should probably forward default construction too. However, no other
facility in the standard does that currently, afaik, so perhaps it would
be palatable not to forward construction of the underlying type, and
provide a make_optional akin to make_shared?

Vladimir Batov

unread,
Jun 5, 2012, 7:42:22 AM6/5/12
to std-pr...@isocpp.org

Indeed, not providing perfect forwarding (PF) would be a safe bet. Having said that I feel that PF is a very important feature that will take C++ to the next level as far as safety and integrity are concerned. shared_ptr and the likes took "delete" out of the picture and now PF will take "new" out (on the application level, anyway). It's really hard to underestimate the importance of this. So, IMO the sooner we start deploying it the better. The "difficulty" though is that PF-enabled classes behaviorally different, i.e. by the old book "optional<Foo> foo;" creates nothing and by the new PF book it calls Foo::Foo(). That will inevitably require some mental re-wiring.

And optional can choose to follow the old school or to lead the way. :-) 

Best,
V.
 

Ville Voutilainen

unread,
Jun 5, 2012, 7:50:59 AM6/5/12
to std-pr...@isocpp.org
On 5 June 2012 14:42, Vladimir Batov <vb.ma...@gmail.com> wrote:
> Indeed, not providing perfect forwarding (PF) would be a safe bet. Having
> said that I feel that PF is a very important feature that will take C++ to
> the next level as far as safety and integrity are concerned. shared_ptr and
> the likes took "delete" out of the picture and now PF will take "new" out
> (on the application level, anyway). It's really hard to underestimate the

Sure. I can already write smart pointers that allocate from a pool, without
users ever seeing placement new and the ugly destructor invocation. It's
a great facility to have.

> importance of this. So, IMO the sooner we start deploying it the better. The
> "difficulty" though is that PF-enabled classes behaviorally different, i.e.
> by the old book "optional<Foo> foo;" creates nothing and by the new PF book
> it calls Foo::Foo(). That will inevitably require some mental re-wiring.
> And optional can choose to follow the old school or to lead the way. :-)

I don't think we should just add that kind of a school of thought by adding
optional so that it's inconsistent with everything else, I think we should
consider real PF facilities as a whole rather than sprinkling utility classes
into the library so that some do PF and some don't.

Dave Abrahams

unread,
Jun 5, 2012, 7:57:29 AM6/5/12
to std-pr...@isocpp.org, std-pr...@isocpp.org


On Jun 5, 2012, at 2:58 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:

Now I believe I can express my intuition more objectively. One way of looking at optional is a variable-size container with the maximum size of 1 element. Variable size containers when default-initialized are empty. If you hold that expectation, you will get confused.

True. However, speaking subjectively, I don't think that expectation is very reasonable. Nor is it suggested by the name "optional."

Sent from my Q-42 Space Modulator

Andrzej Krzemieński

unread,
Jun 5, 2012, 8:32:57 AM6/5/12
to std-pr...@isocpp.org


W dniu wtorek, 5 czerwca 2012 12:54:56 UTC+2 użytkownik Vladimir Batov napisał:
> On Tuesday, June 5, 2012 5:59:40 PM UTC+10, Andrzej Krzemieński wrote:
But on the other hand it poses a number of problems. The type is not regular; you will face a number of problems when you try to use it with STL.

Could you please elaborate what kind of problems that might be. I do not believe STL requires types to be regular.

You know what, I cannot find a single place in STL (container or algorithm) that would not work for non-default-constructible types. I guess I am uder the influence of papers on concepts
(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3351.pdf http://www2.research.att.com/~bs/sle2011-concepts.pdf) that try to minimize the number of concepts and over-constrain the types a bit.

I take back this argument. I start to like the your idea of not providing too much default constructors.

Fernando Cacciola

unread,
Jun 5, 2012, 9:19:58 AM6/5/12
to std-pr...@isocpp.org
>
> I take back this argument. I start to like the your idea of not providing
> too much default constructors.

Please don't :)

There are plenty of use cases where we need optional's default
constructor, even if stuffing it on container is not one of them.
Besides, what the rational for it's absence would be? that we couldn't
agree on what it should do?

Fernando Cacciola

unread,
Jun 5, 2012, 9:37:14 AM6/5/12
to std-pr...@isocpp.org
> optional<char> readChar( Stream str )
> {
>   optional<char> ans;
>
>   if (str.buffered()) {
>     ans = str.readBuffered();
>     clearBuffer();
>   }
>   else if (!str.end()) {
>     ans = str.readChar();
>   }
>
>   return ans;
> }
>
Now that is a counter-case worth considering, specially because of
effect of the "error" would be catastrophic (the entire logic is
broken). And it is definitely important that an API is intuitive and
self-evident as possible.

So, assuming we support perfect forwarding and people get used to it,
what would be the most likely expectation for the behavior of such a
default constructor? I'd think that it is the answer to this question
the principal argument for choosing one behavior or the other.

OTOH, one could argue that on the contrary, for a std facility most
people will eventually just know what the behavior is (then we could
"safely" choose uniformity with PF), so uninformed assumptions are not
that important.

Bottomline: still not sure what is best here...

Andrzej Krzemieński

unread,
Jun 5, 2012, 9:51:09 AM6/5/12
to std-pr...@isocpp.org


W dniu wtorek, 5 czerwca 2012 15:19:58 UTC+2 użytkownik Fernando Cacciola napisał:
>
> I take back this argument. I start to like the your idea of not providing
> too much default constructors.

Please don't :)

There are plenty of use cases where we need optional's default
constructor, even if stuffing it on container is not one of them.

From this discussion I learned that I was confusing "we need to be able to default construct"  with "we need to be able to initialize to disengaged state". with this distinction, the arguments for providing an "unconditional" default constructor appear to me to be dissolving; except perhaps for the readChar example. What other usages can you see?

Besides, what the rational for it's absence would be? that we couldn't
agree on what it should do?
 
In the hypothetical other proposal where we provide a perfect-forwarding constructor (including default construction) and constructor from none, if this fails to compile:

  optional<Model> m;

This is because you know we are perfect forwarding and there was no constructor that we could forward the (empty) argument list to.

Regards,
&rzej
 

Andrzej Krzemieński

unread,
Jun 5, 2012, 10:04:14 AM6/5/12
to std-pr...@isocpp.org

Note that there are two kinds of default constructor and different reasoning applies to either. We have types that default-initialized to a meaningful initial state, like std::mutex, std::vector<int>, and types that remain in fact uninitialized: those with trivial default constructor (ints, pointers, aggregates thereof). What we could do is to refuse to compile the default construction of optional<T> where T is TriviallyDefaultConstructible, or require a special construct if this is really what one intends.

Fernando Cacciola

unread,
Jun 5, 2012, 10:10:11 AM6/5/12
to std-pr...@isocpp.org
On Tue, Jun 5, 2012 at 10:51 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>
> From this discussion I learned that I was confusing "we need to be able to
> default construct"  with "we need to be able to initialize to disengaged
> state". with this distinction, the arguments for providing an
> "unconditional" default constructor appear to me to be dissolving; except
> perhaps for the readChar example. What other usages can you see?
>

Well, just consider for instance a data member. If optional<T> cannot
be default-constructed, neither can the containing class, which would
become an unnecessary impediment on using optional there.

This is in fact one of your arguments: that optional can be used to
"regularize" a type lacking a default constructor.

Dave Abrahams

unread,
Jun 5, 2012, 10:17:31 AM6/5/12
to std-pr...@isocpp.org
For what it's worth, I'm growing more convinced that uniformity should
trump the expectation of emptiness, especially as perfect forwarding
becomes more common and the expectation is correspondingly weakened.

Andrzej Krzemieński

unread,
Jun 5, 2012, 10:17:33 AM6/5/12
to std-pr...@isocpp.org


W dniu wtorek, 5 czerwca 2012 16:10:11 UTC+2 użytkownik Fernando Cacciola napisał:
On Tue, Jun 5, 2012 at 10:51 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>
> From this discussion I learned that I was confusing "we need to be able to
> default construct"  with "we need to be able to initialize to disengaged
> state". with this distinction, the arguments for providing an
> "unconditional" default constructor appear to me to be dissolving; except
> perhaps for the readChar example. What other usages can you see?
>

Well, just consider for instance a data member. If optional<T> cannot
be default-constructed, neither can the containing class, which would
become an unnecessary impediment on using optional there.

This is in fact one of your arguments: that optional can be used to
"regularize" a type lacking a default constructor.
 
It looks like I really shifted my position here. It looks like nobody here really needs to "regularize" the type. You do not prevent default construction of classes that contain the optional because you always have one constructor that is guaranteed to work: the one from none. And using the new member initalization syntax in C++11 you can always declare your class as:

 class Super {
    int i;
    int j;
    optional<Date> o = none;
  };

Fernando Cacciola

unread,
Jun 5, 2012, 10:17:50 AM6/5/12
to std-pr...@isocpp.org
On Tue, Jun 5, 2012 at 11:04 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>
> Note that there are two kinds of default constructor and different reasoning
> applies to either. We have types that default-initialized to a meaningful
> initial state, like std::mutex, std::vector<int>, and types that remain in
> fact uninitialized: those with trivial default constructor (ints, pointers,
> aggregates thereof).

Actually, those types remain uninitialized when default-constructed
but not when value-initialized, and if optional's default constructor
where to forward, then the net effect would not be like:

int o ;

but

int o = int();


In any case, I'm not following you here... how does it matter what
does T's forwarded default constructor, in order to decide on the
behavior of optional's default ctor?

> What we could do is to refuse to compile the default
> construction of optional<T> where T is TriviallyDefaultConstructible

Why will we do that?

Fernando Cacciola

unread,
Jun 5, 2012, 10:23:36 AM6/5/12
to std-pr...@isocpp.org
On Tue, Jun 5, 2012 at 11:17 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:

>
>  class Super {
>     int i;
>     int j;
>     optional<Date> o = none;
>   };

Indeed, when I wrote my response I realized that in the context of
std::optional (i.e > C++11), it is now entirely possible to just do
that.

So one less argument for starting out disengaged and one more for
forwarding the default constructor.

Andrzej Krzemieński

unread,
Jun 5, 2012, 10:32:12 AM6/5/12
to std-pr...@isocpp.org

Sorry. It looks like I am typing faster than I think. For a moment I thought that the problem in readChar was somehow related to the fact that the optionalized type was TriviallyDefaultConstructible, but now I see the same problem would occur if we were dealing with optional<complex<double>>. Please ignore this.

Fernando Cacciola

unread,
Jun 5, 2012, 10:32:35 AM6/5/12
to std-pr...@isocpp.org
On Tue, Jun 5, 2012 at 11:17 AM, Dave Abrahams <da...@boostpro.com> wrote:
>
> For what it's worth, I'm growing more convinced that uniformity should
> trump the expectation of emptiness, especially as perfect forwarding
> becomes more common and the expectation is correspondingly weakened.
>

OK.

How would you rate the likelihood of people repeatedly mistaking the
actual behavior of the default constructor and writing the logic all
wrong (as in the char example)?

I actually prefer to forward to the default constructor, a lot, as it
is IMO the behavior that makes the most sense to me... but I'm worried
about the above, for instance, because that's how Boost.Optional is
used now.

OTOH, if the language were entirely self-evident there wouldn't be
books, conferences, courses and so on.. so maybe I shouldn't worry so
much about it.. this isn't C# or Java (no pun intended)

Andrzej Krzemieński

unread,
Jun 5, 2012, 10:44:50 AM6/5/12
to std-pr...@isocpp.org

Just note that the practical choice, as I understand it, is not between "default initialization to disengaged state" and "uniform perfect initialization", but between "uniform perfect initialization" and "only initialization from T": that is, you have to create the T yourself and give it to optional's constructor (and we may add some convenience functions).

Given that, the current proposal does not meet either of the choices.

Fernando Cacciola

unread,
Jun 5, 2012, 10:47:08 AM6/5/12
to std-pr...@isocpp.org
On Tue, Jun 5, 2012 at 11:32 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>
> Sorry. It looks like I am typing faster than I think. For a moment I thought
> that the problem in readChar was somehow related to the fact that the
> optionalized type was TriviallyDefaultConstructible, but now I see the same
> problem would occur if we were dealing with optional<complex<double>>.
> Please ignore this.

OK
Then just for the record, let me properly post the potential logical
defect that might be introduced if a programmer mistakenly thinks that
a default constructed optional starts disengaged (like it is with
Boost.Optional now):

optional<T> get_if_possible()
{
optional<T> r ; // WATCH-OUT, not disengaged

if ( possible )
r = whatever ;

return r ;
}


void user()
{

optional<T> o = get_if_possible();

if ( o )
use_it(*o);
}


The code above has a serious bug because the getter is NOT returning
none as the programmer clearly intended. he should have written this
instead:

optional<T> r = none ;


And this problem is totally independent of the nature of T. It's about
the behavior or optional's default constructor.


Granted, one can construct the exact opposite use case and favor the
other choice by the same reasoning:

optional< vector<int> > get_if_possible()
{
return possible ? optional< vector<int> >() : none ;
}

But, I find this second use-case quite unlikely and artificial, while
the former is very common in real world cases since that's how
Boost.Optional is currently used.

In fact, if I would want to replace my uses of Boost.Optional for
std::optional, I would have to go over hunderd of source files and
replace each default constructed boost::optional<> for one
initialized to none.

Best

Fernando Cacciola

unread,
Jun 5, 2012, 10:49:10 AM6/5/12
to std-pr...@isocpp.org
On Tue, Jun 5, 2012 at 11:44 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>
>
> Just note that the practical choice, as I understand it, is not between
> "default initialization to disengaged state" and "uniform perfect
> initialization", but between "uniform perfect initialization" and "only
> initialization from T": that is, you have to create the T yourself and give
> it to optional's constructor (and we may add some convenience functions).
>

Can you elaborate on that?

I would have said exactly the opposite (that the choices are between
your first two)

Andrzej Krzemieński

unread,
Jun 5, 2012, 10:54:42 AM6/5/12
to std-pr...@isocpp.org

This issue would be slightly mitigated if we proposed a tool with a different name, like (following Haskell) maybe<T>. Then at least no-one would expect Boost.Optional's behavior.

Fernando Cacciola

unread,
Jun 5, 2012, 11:06:59 AM6/5/12
to std-pr...@isocpp.org
>>
>> In fact, if I would want to replace my uses of Boost.Optional for
>> std::optional, I would have to go over hunderd of source files and
>> replace each default constructed boost::optional<>  for one
>> initialized to none.
>
>
> This issue would be slightly mitigated if we proposed a tool with a
> different name, like (following Haskell) maybe<T>. Then at least no-one
> would expect Boost.Optional's behavior.
>

True.

OTOH, the actual mechanics of the replacement would still be required,
unless I choose to stick with boost::optional<>, which I can actually
do just as well even if we proposed std::optional<>, so I don't think
changing the name is really useful.

Also don't get me wrong, I didn't mean to imply that because of that I
prefer the choice of disengaged initial state. I was just putting
relevant facts on the table.

FWIW, when the STL came along I had my own container types all over
the many projects I worked with at the time. But my design had a
radical difference: Container c(10) just meant that space for 10
elements was reserved. None where added just yet (unlike the case of
std::vector c(10)). so I had to go all over each place (a few hundred)
and change that when I adopted the STL for the first time. IME is not
that bad (and that was 14 years ago, so now it should be a piece of
cake)

Ville Voutilainen

unread,
Jun 5, 2012, 11:30:10 AM6/5/12
to std-pr...@isocpp.org
On 5 June 2012 17:49, Fernando Cacciola <fernando...@gmail.com> wrote:
>> Just note that the practical choice, as I understand it, is not between
>> "default initialization to disengaged state" and "uniform perfect
>> initialization", but between "uniform perfect initialization" and "only
>> initialization from T": that is, you have to create the T yourself and give
>> it to optional's constructor (and we may add some convenience functions).
> Can you elaborate on that?
> I would have said exactly the opposite (that the choices are between
> your first two)

As I said before, that is imho the *practical* choice - to forward or
not to forward.
Then we get to whether to forward perfectly, or whether default-init
of an optional
doesn't forward but creates a disengaged optional. And, as I said before, if
we forward, I think we should forward perfectly. We can decide not to forward
at all, in which case

optional<T> o;

creates a disengaged optional, and if you want it to do something else, you get
to invoke in-place construction somehow, or pass in an actual T. Even so,
the in-place case needs to forward the arguments. That's different from generic
perfect forwarding.

If we forward, how come I shouldn't then think that

optional<T> o{none};

should be calling the constructor of T with none as the argument
rather than creating a disengaged optional?

I may be a bit confused by all this. :) The amount of confusion would
be seriously
decreased if we don't play forwarding tricks in the constructors of optional.

Andrzej Krzemieński

unread,
Jun 5, 2012, 12:06:57 PM6/5/12
to std-pr...@isocpp.org


W dniu wtorek, 5 czerwca 2012 16:49:10 UTC+2 użytkownik Fernando Cacciola napisał:
On Tue, Jun 5, 2012 at 11:44 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>
>
> Just note that the practical choice, as I understand it, is not between
> "default initialization to disengaged state" and "uniform perfect
> initialization", but between "uniform perfect initialization" and "only
> initialization from T": that is, you have to create the T yourself and give
> it to optional's constructor (and we may add some convenience functions).
>
Can you elaborate on that?

I would have said exactly the opposite (that the choices are between
your first two)

Ville has put it right. We have, in total, three options:
(1) Do uniform perfect forwarding. (then default ctor creates _an engaged_ optional)
(2) Do non-uniform, almost perfect forwarding with default ctor creating a disengaged object
(3) Do no perfect forwarding at all  
 
My Impression was that Dave was comparing (1) and (2). since I (and I guess the majority) do not consider (2) a viable option, I believe that the only options worth comparing are (1) and (3). Option (3) severely compromises some usage cases but it does not have any gotchas and it would provide an interface "compatible" (in some sense) with variant<T>.


Fernando Cacciola

unread,
Jun 5, 2012, 12:17:52 PM6/5/12
to std-pr...@isocpp.org
On Tue, Jun 5, 2012 at 1:06 PM, Andrzej Krzemieński <akrz...@gmail.com> wrote:

> Ville has put it right. We have, in total, three options:
> (1) Do uniform perfect forwarding. (then default ctor creates _an engaged_
> optional)
> (2) Do non-uniform, almost perfect forwarding with default ctor creating a
> disengaged object
> (3) Do no perfect forwarding at all
>
OK

> My Impression was that Dave was comparing (1) and (2)

And so was I.

> since I (and I guess
> the majority) do not consider (2) a viable option

Why not?

> I believe that the only
> options worth comparing are (1) and (3). Option (3) severely compromises
> some usage cases but it does not have any gotchas and it would provide an
> interface "compatible" (in some sense) with variant<T>.
>
Agreed that (3) sort of solves the problem leaves (1), as originally
proposed the only logical behaviour.

OTOH, this whole sub-thread started because a forwarding ctor is a
really useful feature, so I'm still debating between (1) and (2),

What Ville pointed out, that (2) is less perfect that (1), is an
argument against (2), but the "potential bug" we described before is
an argument for it.

Fernando Cacciola

unread,
Jun 5, 2012, 12:20:03 PM6/5/12
to std-pr...@isocpp.org
let me correct myself:

> Agreed that (3) sort of solves the problem leaves (1), as originally
> proposed the only logical behaviour.
>
I meant that (3) leaves "a default ctor that starts disengaged" as the
only logical choice.

That (1) in there was misplaced.

Dave Abrahams

unread,
Jun 5, 2012, 12:23:34 PM6/5/12
to std-pr...@isocpp.org
there is also the possibility of a shared_ptr-like
make_optional<T>(...). Of course, that would only be efficient for Ts
with an inexpensive move operation.

Andrzej Krzemieński

unread,
Jun 5, 2012, 12:34:27 PM6/5/12
to std-pr...@isocpp.org


W dniu wtorek, 5 czerwca 2012 18:17:52 UTC+2 użytkownik Fernando Cacciola napisał:
On Tue, Jun 5, 2012 at 1:06 PM, Andrzej Krzemieński <akrz...@gmail.com> wrote:

> Ville has put it right. We have, in total, three options:
> (1) Do uniform perfect forwarding. (then default ctor creates _an engaged_
> optional)
> (2) Do non-uniform, almost perfect forwarding with default ctor creating a
> disengaged object
> (3) Do no perfect forwarding at all


> since I (and I guess
> the majority) do not consider (2) a viable option

Why not?
 
Due to surprising effects of non-uniformity:

  template <class ...A>
  void fwd(const A&&... a)
  {
    optional<vector<int>> o = {a...};
    assert (bool(o)); // not true for empty a
  }

In such generic contexts, when you see the ellipsis you expect a number of arguments, and would be surprised that the assertion fired.

On the other hand, perhaps this surprise is no worse than the default-initialized engaged state. Ehh...

Jeffrey Yasskin

unread,
Jun 5, 2012, 1:25:57 PM6/5/12
to std-pr...@isocpp.org
On Tue, Jun 5, 2012 at 9:06 AM, Andrzej Krzemieński <akrz...@gmail.com> wrote:
>
>
> W dniu wtorek, 5 czerwca 2012 16:49:10 UTC+2 użytkownik Fernando Cacciola
> napisał:
>>
>> On Tue, Jun 5, 2012 at 11:44 AM, Andrzej Krzemieński <akrz...@gmail.com>
>> wrote:
>> >
>> >
>> > Just note that the practical choice, as I understand it, is not between
>> > "default initialization to disengaged state" and "uniform perfect
>> > initialization", but between "uniform perfect initialization" and "only
>> > initialization from T": that is, you have to create the T yourself and
>> > give
>> > it to optional's constructor (and we may add some convenience
>> > functions).
>> >
>> Can you elaborate on that?
>>
>> I would have said exactly the opposite (that the choices are between
>> your first two)
>
>
> Ville has put it right. We have, in total, three options:
> (1) Do uniform perfect forwarding. (then default ctor creates _an engaged_
> optional)
> (2) Do non-uniform, almost perfect forwarding with default ctor creating a
> disengaged object
> (3) Do no perfect forwarding at all

4 options. Or did I miss a technical argument that the prefix that's
currently in the paper doesn't work? There's nothing less "perfect"
about the forwarding with a prefix argument, it's just a bit uglier,
and only in cases that most users won't need to use.

Jeffrey

Andrzej Krzemieński

unread,
Jun 5, 2012, 2:31:20 PM6/5/12
to std-pr...@isocpp.org

The "prefixed perfect forwrding" would still be provided as part of (3), I guess. It would still be imperfect because it could not be used in copy-initialization contexts wherever T is replaced with optional<T>.
It is loading more messages.
0 new messages