44d57a1d8b
Older MSVC versions add illegal temporary when you want to assign from const integral value.
124 lines
4.7 KiB
Plaintext
124 lines
4.7 KiB
Plaintext
|
|
[section Optional references]
|
|
|
|
[section Overview]
|
|
|
|
This library allows the template parameter `T` to be of reference type:
|
|
`T&`, and to some extent, `T const&`.
|
|
|
|
However, since references are not real objects some restrictions apply and
|
|
some operations are not available in this case:
|
|
|
|
* Converting constructors
|
|
* Converting assignment
|
|
* InPlace construction
|
|
* InPlace assignment
|
|
* Value-access via pointer
|
|
|
|
Also, even though `optional<T&>` treats it wrapped pseudo-object much as
|
|
a real value, a true real reference is stored so aliasing will ocurr:
|
|
|
|
* Copies of `optional<T&>` will copy the references but all these references
|
|
will nonetheless refer to the same object.
|
|
* Value-access will actually provide access to the referenced object
|
|
rather than the reference itself.
|
|
|
|
[caution On compilers that do not conform to Standard C++ rules of reference binding, some operations on optional references are disabled in order to prevent subtle bugs. For more details see [link boost_optional.dependencies_and_portability.optional_reference_binding Dependencies and Portability section].]
|
|
|
|
[heading Rvalue references]
|
|
|
|
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.
|
|
|
|
const int& i = 1; // legal
|
|
optional<const int&> oi = 1; // illegal
|
|
|
|
[endsect]
|
|
|
|
[section Rebinding semantics for assignment of optional references]
|
|
|
|
If you assign to an ['uninitialized ] `optional<T&>` the effect is to bind (for
|
|
the first time) to the object. Clearly, there is no other choice.
|
|
|
|
int x = 1 ;
|
|
int& rx = x ;
|
|
optional<int&> ora ;
|
|
optional<int&> orb(x) ;
|
|
ora = orb ; // now 'ora' is bound to 'x' through 'rx'
|
|
*ora = 2 ; // Changes value of 'x' through 'ora'
|
|
assert(x==2);
|
|
|
|
If you assign to a bare C++ reference, the assignment is forwarded to the
|
|
referenced object; its value changes but the reference is never rebound.
|
|
|
|
int a = 1 ;
|
|
int& ra = a ;
|
|
int b = 2 ;
|
|
int& rb = b ;
|
|
ra = rb ; // Changes the value of 'a' to 'b'
|
|
assert(a==b);
|
|
b = 3 ;
|
|
assert(ra!=b); // 'ra' is not rebound to 'b'
|
|
|
|
Now, if you assign to an ['initialized ] `optional<T&>`, the effect is to
|
|
[*rebind] to the new object instead of assigning the referee. This is unlike
|
|
bare C++ references.
|
|
|
|
int a = 1 ;
|
|
int b = 2 ;
|
|
int& ra = a ;
|
|
int& rb = b ;
|
|
optional<int&> ora(ra) ;
|
|
optional<int&> orb(rb) ;
|
|
ora = orb ; // 'ora' is rebound to 'b'
|
|
*ora = 3 ; // Changes value of 'b' (not 'a')
|
|
assert(a==1);
|
|
assert(b==3);
|
|
|
|
[heading Rationale]
|
|
|
|
Rebinding semantics for the assignment of ['initialized ] `optional` references has
|
|
been chosen to provide [*consistency among initialization states] even at the
|
|
expense of lack of consistency with the semantics of bare C++ references.
|
|
It is true that `optional<U>` strives to behave as much as possible as `U`
|
|
does whenever it is initialized; but in the case when `U` is `T&`, doing so would
|
|
result in inconsistent behavior w.r.t to the lvalue initialization state.
|
|
|
|
Imagine `optional<T&>` forwarding assignment to the referenced object (thus
|
|
changing the referenced object value but not rebinding), and consider the
|
|
following code:
|
|
|
|
optional<int&> a = get();
|
|
int x = 1 ;
|
|
int& rx = x ;
|
|
optional<int&> b(rx);
|
|
a = b ;
|
|
|
|
What does the assignment do?
|
|
|
|
If `a` is ['uninitialized], the answer is clear: it binds to `x` (we now have
|
|
another reference to `x`).
|
|
But what if `a` is already ['initialized]? it would change the value of the
|
|
referenced object (whatever that is); which is inconsistent with the other
|
|
possible case.
|
|
|
|
If `optional<T&>` would assign just like `T&` does, you would never be able to
|
|
use Optional's assignment without explicitly handling the previous
|
|
initialization state unless your code is capable of functioning whether
|
|
after the assignment, `a` aliases the same object as `b` or not.
|
|
|
|
That is, you would have to discriminate in order to be consistent.
|
|
|
|
If in your code rebinding to another object is not an option, then it is very
|
|
likely that binding for the first time isn't either. In such case, assignment
|
|
to an ['uninitialized ] `optional<T&>` shall be prohibited. It is quite possible
|
|
that in such a scenario it is a precondition that the lvalue must be already
|
|
initialized. If it isn't, then binding for the first time is OK
|
|
while rebinding is not which is IMO very unlikely.
|
|
In such a scenario, you can assign the value itself directly, as in:
|
|
|
|
assert(!!opt);
|
|
*opt=value;
|
|
|
|
[endsect]
|
|
[endsect]
|