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...
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?
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?
void fun()
{
optional<GlobalLock> g(in_place);
// use g
g = none;
// do other stuff
}
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.
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.
none_t and T" or, if I can use Boost's nomenclature: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."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.
However, it might be even better if we could make:optional<A> x({42, 3.14});
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.)
At brief glance:
"Copy assignment ofoptional<T>provides the same exception guarantee as copy assignment ofT. Move assignment ofoptional<T>provides the same exception guarantee as move assignment ofT."
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?
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.)
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.
optional<A> x(42, 3.14);
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 probably misssed it somewhere back there in the flood of earliermessages, 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?
, 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.
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.
ExpensiveDefaultCtorarray[100]; // 100 default constructions std::array<ExpensiveDefaultCtor, 100> arr; // 100 default constructions std::vector<ExpensiveDefaultCtor> vec(100); // 100 default constructions
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.
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
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.
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>>.
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."
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.
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.
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.
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.
The non-uniformity problem could be solved the other way around: disallow any perfect forwarding constructors.
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.
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?).
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...
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.
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
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.
void fun()
{
optional<Model> m{param1, param2};
// ...
}
If you know you want to initialize the contained value, why not just use non-optional Model?
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.
...
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.
> 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.
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
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.
...
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.
...
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.
...
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.
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.
> 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.
>
> 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?
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.
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)
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?
template <class ...A>
void fwd(const A&&... a)
{
optional<vector<int>> o = {a...};
assert (bool(o)); // not true for empty a
}