Skip to content

Commit 2e583aa

Browse files
committed
Fixed code, updated docs, added emplace()
1 parent 22baf1d commit 2e583aa

23 files changed

+985
-203
lines changed

doc/optional.qbk renamed to doc/00_optional.qbk

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,39 @@ Distributed under the Boost Software License, Version 1.0.
4646

4747

4848
[section Introduction]
49-
This library can be used to represent 'optional' (or 'nullable') objects that can be safely passed by value:
49+
Class template `optional` is a wrapper for representing 'optional' (or 'nullable') objects who may not (yet) contain a valid value. Optional objects offer full value semantics; they are good for passing by value and usage inside STL containers. This is a header-only library.
5050

51-
optional<int> readInt(); // this function may return either an int or a not-an-int
51+
[section Problem]
52+
Suppose we want to read a parameter form a config file which represents some integral value, let's call it `"MaxValue"`. It is possible that this parameter is not specified; such situation is no error. It is valid to not specify the parameter and in that case the program is supposed to behave slightly different. Also suppose that any possible value of type `int` is a valid value for `"MaxValue"`, so we cannot jut use `-1` to represent the absence of the parameter in the config file.
53+
[endsect]
54+
55+
[section Solution]
56+
57+
This is how you solve it with `boost::optional`:
58+
59+
#include <boost/optional.hpp>
60+
61+
boost::optional<int> getConfigParam(std::string name); // return either an int or a `not-an-int`
5262

53-
if (optional<int> oi = readInt()) // did I get a real int
54-
cout << "my int is: " << *oi; // use my int
55-
else
56-
cout << "I have no int";
57-
63+
int main()
64+
{
65+
if (boost::optional<int> oi = getConfigParam("MaxValue")) // did I get a real int?
66+
runWithMax(*oi); // use my int
67+
else
68+
runWithNoMax();
69+
}
70+
71+
[endsect]
5872
[endsect]
5973

60-
[include motivation.qbk]
61-
[include development.qbk]
62-
[include reference.qbk]
63-
[include examples.qbk]
64-
[include special_cases.qbk]
65-
[include dependencies.qbk]
66-
[include acknowledgments.qbk]
74+
[include 01_tutorial.qbk]
75+
[include 02_discussion.qbk]
76+
[include 03_development.qbk]
77+
[include 04_reference.qbk]
78+
[include 05_examples.qbk]
79+
[include 10_optional_references.qbk]
80+
[include 11_special_cases.qbk]
81+
[include 90_dependencies.qbk]
82+
[include 91_acknowledgments.qbk]
6783

6884

doc/01_tutorial.qbk

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
[/
2+
Boost.Optional
3+
4+
Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal
5+
Copyright (c) 2014 Andrzej Krzemienski
6+
7+
Distributed under the Boost Software License, Version 1.0.
8+
(See accompanying file LICENSE_1_0.txt or copy at
9+
http://www.boost.org/LICENSE_1_0.txt)
10+
]
11+
12+
13+
[section Tutorial]
14+
15+
[section Optional return values]
16+
17+
Let's write and use a converter function that converts an a `std::string` to an `int`. It is possible that for a given string (e.g. `"cat"`) there exist no value of type `int` capable of representing the conversion result. We do not consider such situation an error. We expect that the converter can be used only to check if the conversion is possible. A natural signature for this function can be:
18+
19+
#include <boost/optional.hpp>
20+
boost::optionl<int> convert(const std::string& text);
21+
22+
All necessary functionality can be included with one header `<boost/optional.hpp>`. The above function signature means that the function can either return a value of type `int` or a flag indicating that no value of `int` is available. This does not indicate an error. It is like one additional value of `int`. This is how we can use our function:
23+
24+
const std::string& text = /*... */;
25+
boost::optionl<int> oi = convert(text); // move-construct
26+
if (oi) // contextual conversion to bool
27+
int i = *oi; // operator*
28+
29+
In order to test if `optional` contains a value, we use the contextual conversion to type `bool`. Because of this we can combine the initialization of the optional object and the test into one instruction:
30+
31+
if (boost::optionl<int> oi = convert(text))
32+
int i = *oi;
33+
34+
We extract the contained value with `operator*` (and with `operator->` where it makes sense). An attempt to extract the contained value of an uninitialized optional object is an ['undefined behaviour] (UB). This implementation guards the call with `BOOST_ASSERT`. Therefore you should be sure that the contained value is there before extracting. For instance, the following code is reasonably UB-safe:
35+
36+
int i = *convert("100");
37+
38+
This is because we know that string value `"100"` converts to a valid value of `int`. If you do not like this potential UB, you can use an alternative way of extracting the contained value:
39+
40+
try {
41+
int j = convert(text).value();
42+
}
43+
catch (const boost::bad_optional_access&) {
44+
// deal with it
45+
}
46+
47+
This version throws an exception upon an attempt to access a non-existent contained value. If your way of dealing with the missing value is to use some default, like `0`, there exists a yet another alternative:
48+
49+
int k = convert(text).value_or(0);
50+
51+
This uses the `atoi`-like approach to conversions: if `text` does not represent an integral number just return `0`. Now, let's consider how function `convert` can be implemented.
52+
53+
boost::optionl<int> convert(const std::string& text)
54+
{
55+
std::stringstream s(text);
56+
int i;
57+
if ((s >> i) && s.get() == std::char_traits<char>::eof())
58+
return i;
59+
else
60+
return boost::none;
61+
}
62+
63+
Observe the two return statements. `return i` uses the converting constructor that can create `optional<T>` from `T`. Thus constructed optional object is initialized and its value is a copy of `i`. The other return statement uses another converting constructor from a special tag `boost::none`. It is used to indicate that we want to create an uninitialized optional object.
64+
65+
66+
[endsect]
67+
68+
[section Optional data members]
69+
Suppose we want to implement a ['lazy load] optimization. This is because we do not want to perform an expensive initialization of our `Resource` until (if at all) it is really used. We can do it this way:
70+
71+
class Widget
72+
{
73+
boost::optional<Resource> resource_;
74+
75+
public:
76+
Widget() {}
77+
78+
Resource& getResource() // not thread-safe
79+
{
80+
if (resource_ == boost::none)
81+
resource_.emplace("resource", "arguments");
82+
83+
return *resource_;
84+
}
85+
};
86+
87+
`optional`'s default constructor creates an uninitialized optional. No call to `Resource`'s default constructor is attempted. `Resource` doesn't have to be __SGI_DEFAULT_CONSTRUCTIBLE__. In function `getResource` we first check if `resource_` is initialized. This time we do not use the contextual conversion to `bool`, but a comparison with `boost::none`. These two ways are equivalent. Function `emplace` initializes the optional in-place by perfect-forwarding the arguments to the constructor of `Resource`. No copy- or move-construction is involved here. `Resource` doesn't even have to be `MoveConstructible`.
88+
89+
[note Function `emplace` is only available on compilers that support rvalue references and variadic templates. If your compiler does not support these features and you still need to avoid any move-constructions, use [link boost_optional.in_place_factories In-Place Factories].]
90+
91+
[endsect]
92+
93+
[section Bypassing unnecessary default construction]
94+
Suppose we have class `Date`, which does not have a default constructor: there is no good candidate for a default date. We have a function that returns two dates in form of a `boost::tuple`:
95+
96+
boost::tuple<Date, Date> getPeriod();
97+
98+
In other place we want to use the result of `getPeriod`, but want the two dates to be named: `begin` and `end`. We want to implement something like 'multiple return values':
99+
100+
Date begin, end; // Error: no default ctor!
101+
boost::tie(begin, end) = getPeriod();
102+
103+
The second line works already, this is the capability of Boost.Tuple library, but the first line won't work. We could set some initial invented dates, but it is confusing and may be an unacceptable cost, given that these values will be overwritten in the next line anyway. This is where `optional` can help:
104+
105+
boost::optional<Date> begin, end;
106+
boost::tie(begin, end) = getPeriod();
107+
108+
It works because inside `boost::tie` a move-assignment from `T` is invoked on `optional<T>`, which internally calls a move-constructor of `T`.
109+
[endsect]
110+
111+
[endsect]
112+
113+
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
http://www.boost.org/LICENSE_1_0.txt)
1010
]
1111

12-
[section Motivation]
12+
[section Discussion]
1313

1414
Consider these functions which should return a value but which might not have
1515
a value to return:
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
[section The models]
1414

1515
In C++, we can ['declare] an object (a variable) of type `T`, and we can give this
16-
variable an ['initial value] (through an ['initializer]. (c.f. 8.5)).
16+
variable an ['initial value] (through an ['initializer]. (cf. 8.5)).
1717
When a declaration includes a non-empty initializer (an initial value is given),
1818
it is said that the object has been initialized.
1919
If the declaration uses an empty initializer (no initial value is given), and
2020
neither default nor value initialization applies, it is said that the object is
2121
[*uninitialized]. Its actual value exist but has an ['indeterminate initial value]
22-
(c.f. 8.5.9).
22+
(cf. 8.5/11).
2323
`optional<T>` intends to formalize the notion of initialization (or lack of it)
2424
allowing a program to test whether an object has been initialized and stating
2525
that access to the value of an uninitialized object is undefined behavior. That
@@ -38,7 +38,7 @@ additional information to tell if an object has been effectively initialized.
3838
One of the typical ways in which this has been historically dealt with is via
3939
a special value: `EOF`, `npos`, -1, etc... This is equivalent to adding the
4040
special value to the set of possible values of a given type. This super set of
41-
`T` plus some ['nil_t]—were `nil_t` is some stateless POD-can be modeled in modern
41+
`T` plus some ['nil_t]—where `nil_t` is some stateless PODcan be modeled in modern
4242
languages as a [*discriminated union] of T and nil_t. Discriminated unions are
4343
often called ['variants]. A variant has a ['current type], which in our case is either
4444
`T` or `nil_t`.
@@ -197,7 +197,7 @@ be undefined unless the implied pointee actually exist.
197197
Such a ['de facto] idiom for referring to optional objects can be formalized
198198
in the form of a concept: the __OPTIONAL_POINTEE__ concept.
199199
This concept captures the syntactic usage of operators `*`, `->` and
200-
conversion to `bool` to convey the notion of optionality.
200+
contextual conversion to `bool` to convey the notion of optionality.
201201

202202
However, pointers are good to [_refer] to optional objects, but not particularly
203203
good to handle the optional objects in all other respects, such as initializing
Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
template<class U> optional& operator = ( optional<U> const& rhs ) ; ``[link reference_optional_operator_equal_other_optional __GO_TO__]``
5959

6060
template<class U> optional& operator = ( optional<U>&& rhs ) ; ``[link reference_optional_operator_move_equal_other_optional __GO_TO__]``
61+
62+
template<class... Args> void emplace ( Args...&& args ) ; ``[link reference_optional_emplace __GO_TO__]``
6163

6264
template<class InPlaceFactory> optional& operator = ( InPlaceFactory const& f ) ; ``[link reference_optional_operator_equal_factory __GO_TO__]``
6365

@@ -746,6 +748,21 @@ assert ( *opt1 == static_cast<U>(v) ) ;
746748

747749
__SPACE__
748750

751+
[#reference_optional_emplace]
752+
753+
[: `template<class... Args> void optional<T` ['(not a ref)]`>::emplace( Args...&& args );`]
754+
755+
* [*Requires:] The compiler supports rvalue references and variadic templates.
756+
* [*Effect:] If `*this` is initialized calls `*this = none`.
757+
Then initializes in-place the contained value as if direct-initializing an object
758+
of type `T` with `std::forward<Args>(args)...`.
759+
* [*Postconditions: ] `*this` is [_initialized].
760+
* [*Throws:] Whatever the selected `T`'s constructor throws.
761+
* [*Notes:] `T` need not be `MoveConstructible` or `MoveAssignable`.
762+
* [*Exception Safety:] If an exception is thrown during the initialization of `T`, `*this` is ['uninitialized].
763+
764+
__SPACE__
765+
749766
[#reference_optional_operator_equal_factory]
750767

751768
[: `template<InPlaceFactory> optional<T>& optional<T` ['(not a ref)]`>::operator=( InPlaceFactory const& f );`]
@@ -823,7 +840,7 @@ try {
823840
assert ( false );
824841
}
825842
catch(bad_optional_access&) {
826-
asert ( true );
843+
assert ( true );
827844
}
828845
``
829846
__SPACE__
File renamed without changes.

doc/10_optional_references.qbk

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+

2+
[section Optional references]
3+
4+
This library allows the template parameter `T` to be of reference type:
5+
`T&`, and to some extent, `T const&`.
6+
7+
However, since references are not real objects some restrictions apply and
8+
some operations are not available in this case:
9+
10+
* Converting constructors
11+
* Converting assignment
12+
* InPlace construction
13+
* InPlace assignment
14+
* Value-access via pointer
15+
16+
Also, even though `optional<T&>` treats it wrapped pseudo-object much as
17+
a real value, a true real reference is stored so aliasing will ocurr:
18+
19+
* Copies of `optional<T&>` will copy the references but all these references
20+
will nonetheless refer to the same object.
21+
* Value-access will actually provide access to the referenced object
22+
rather than the reference itself.
23+
24+
[warning On compilers that do not conform to Standard C++ rules of reference binding, operations on optional references might give adverse results: rather than binding a reference to a designated object they may create an unexpected temporary and bind to it. For more details see [link boost_optional.dependencies_and_portability.optional_reference_binding Dependencies and Portability section].]
25+
26+
[heading Rvalue references]
27+
28+
Rvalue references and lvalue references to const have the ability in C++ to extend the life time of a temporary they bind to. Optional references do not have this capability, therefore to avoid surprising effects it is not possible to initialize an optional references from a temporary. Optional rvalue references are disabled altogether. Also, the initialization and assignment of an optional reference to const from rvalue reference is disabled.
29+
30+
const int& i = 1; // legal
31+
optional<const int&> oi = 1; // illegal
32+
33+
[endsect]
34+
35+
[section Rebinding semantics for assignment of optional references]
36+
37+
If you assign to an ['uninitialized ] `optional<T&>` the effect is to bind (for
38+
the first time) to the object. Clearly, there is no other choice.
39+
40+
int x = 1 ;
41+
int& rx = x ;
42+
optional<int&> ora ;
43+
optional<int&> orb(x) ;
44+
ora = orb ; // now 'ora' is bound to 'x' through 'rx'
45+
*ora = 2 ; // Changes value of 'x' through 'ora'
46+
assert(x==2);
47+
48+
If you assign to a bare C++ reference, the assignment is forwarded to the
49+
referenced object; its value changes but the reference is never rebound.
50+
51+
int a = 1 ;
52+
int& ra = a ;
53+
int b = 2 ;
54+
int& rb = b ;
55+
ra = rb ; // Changes the value of 'a' to 'b'
56+
assert(a==b);
57+
b = 3 ;
58+
assert(ra!=b); // 'ra' is not rebound to 'b'
59+
60+
Now, if you assign to an ['initialized ] `optional<T&>`, the effect is to
61+
[*rebind] to the new object instead of assigning the referee. This is unlike
62+
bare C++ references.
63+
64+
int a = 1 ;
65+
int b = 2 ;
66+
int& ra = a ;
67+
int& rb = b ;
68+
optional<int&> ora(ra) ;
69+
optional<int&> orb(rb) ;
70+
ora = orb ; // 'ora' is rebound to 'b'
71+
*ora = 3 ; // Changes value of 'b' (not 'a')
72+
assert(a==1);
73+
assert(b==3);
74+
75+
[heading Rationale]
76+
77+
Rebinding semantics for the assignment of ['initialized ] `optional` references has
78+
been chosen to provide [*consistency among initialization states] even at the
79+
expense of lack of consistency with the semantics of bare C++ references.
80+
It is true that `optional<U>` strives to behave as much as possible as `U`
81+
does whenever it is initialized; but in the case when `U` is `T&`, doing so would
82+
result in inconsistent behavior w.r.t to the lvalue initialization state.
83+
84+
Imagine `optional<T&>` forwarding assignment to the referenced object (thus
85+
changing the referenced object value but not rebinding), and consider the
86+
following code:
87+
88+
optional<int&> a = get();
89+
int x = 1 ;
90+
int& rx = x ;
91+
optional<int&> b(rx);
92+
a = b ;
93+
94+
What does the assignment do?
95+
96+
If `a` is ['uninitialized], the answer is clear: it binds to `x` (we now have
97+
another reference to `x`).
98+
But what if `a` is already ['initialized]? it would change the value of the
99+
referenced object (whatever that is); which is inconsistent with the other
100+
possible case.
101+
102+
If `optional<T&>` would assign just like `T&` does, you would never be able to
103+
use Optional's assignment without explicitly handling the previous
104+
initialization state unless your code is capable of functioning whether
105+
after the assignment, `a` aliases the same object as `b` or not.
106+
107+
That is, you would have to discriminate in order to be consistent.
108+
109+
If in your code rebinding to another object is not an option, then it is very
110+
likely that binding for the first time isn't either. In such case, assignment
111+
to an ['uninitialized ] `optional<T&>` shall be prohibited. It is quite possible
112+
that in such a scenario it is a precondition that the lvalue must be already
113+
initialized. If it isn't, then binding for the first time is OK
114+
while rebinding is not which is IMO very unlikely.
115+
In such a scenario, you can assign the value itself directly, as in:
116+
117+
assert(!!opt);
118+
*opt=value;
119+
120+
[endsect]

0 commit comments

Comments
 (0)