13

std::optional<int&> xx; just doesn't compile for the latest gcc-7.0.0 snapshot. Does the C++17 standard include std::optional for references? And why if it doesn't? (The implementation with pointers in a dedicated specialization whould cause no problems i guess.)

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Vahagn
  • 4,670
  • 9
  • 43
  • 72
  • 1
    @Someprogrammerdude That's not how `optional` works. Otherwise, you couldn't have `optional` either. You could write an optional type that supports reference types. Boost's does. The standard just chooses not to (due to `operator=`). – Barry Nov 02 '16 at 15:05
  • 3
    Pretty much the entire point of references is, _they're not optional_. – jthill Nov 02 '16 at 21:47
  • 2
    How about `std::optional< std::reference_wrapper >` :-D – M.M Nov 02 '16 at 21:51
  • @M.M Actually in my code it turned out more convenient to do `std::optoinal>` ։) – Vahagn Nov 08 '16 at 14:50
  • Possible duplicate of [std::optional specialization for reference types](https://stackoverflow.com/q/26858034/608639) – jww Sep 21 '18 at 00:24

2 Answers2

19

Because optional, as standardized in C++17, does not permit reference types. This was excluded by design.

There are two reasons for this. The first is that, structurally speaking, an optional<T&> is equivalent to a T*. They may have different interfaces, but they do the same thing.

The second thing is that there was effectively no consensus by the standards committee on questions of exactly how optional<T&> should behave.

Consider the following:

optional<T&> ot = ...;
T t = ...;
ot = t;

What should that last line do? Is it taking the object being referenced by ot and copy-assign to it, such that *ot == t? Or should it rebind the stored reference itself, such that ot.get() == &t? Worse, will it do different things based on whether ot was engaged or not before the assignment?

Some people will expect it to do one thing, and some people will expect it to do the other. So no matter which side you pick, somebody is going to be confused.

If you had used a T* instead, it would be quite clear which happens:

T* pt = ...;
T t = ...;
pt = t;   //Compile error. Be more specific.
*pt = t;  //Assign to pointed-to object.
pt = &t;  //Change pointer.
Community
  • 1
  • 1
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Well, I wouldn't accept the first reason, as i need a uniform interface in a generic code. As for the second reason, for me it should definitely behave like `*ot = t`. By the way, is it the case for `boost::optional`? – Vahagn Nov 02 '16 at 16:14
  • 2
    "So no matter which side you pick, somebody is going to be confused." -- So the solution is to make the both sides confused right? ։) – Vahagn Nov 02 '16 at 16:15
  • If `ot` is not engaged I would even throw an exception while `ot = t`. – Vahagn Nov 02 '16 at 16:21
  • 1
    @Vahagn: "*for me it should definitely behave like `*ot = t`.*" If you do that, how would you rebind a reference? Set it to NULL and then set it to the new reference? And how would the latter work? What if you have an `optional>`? Would doing `ot = nullopt` disengage the outer wrapper or the inner wrapper? – Nicol Bolas Nov 02 '16 at 16:55
  • @NicolBolas simply by saying `o = t` it will rebind and `*o = t` it will assign to the reference. They also have the `optional` confusion and decided that operating on the optional directly will use the optional choice, and to operate on the value, you have to dereference. After all, if you say `optional o=1; o = nullopt`, then it also will compile, inspite the fact that `nullopt` is not convertible to `int`. – Johannes Schaub - litb Jul 18 '17 at 15:20
  • @JohannesSchaub-litb: Maybe that's the way to go. But even if it is, the fact remains that this issue was what did not gain consensus in the C++ standards committee. And therefore, this is why `optional` is not in C++17. – Nicol Bolas Jul 18 '17 at 19:20
  • 1
    In my opinion. `optional` is NOT equivalent to a `T*`. In modern C++ thoughts, if you return a `T*`, the caller may think whether I should free the pointer after I use it. The raw pointer may confuse by who is the ownership of the object. But if you return a `optional`, the caller will not deal with the optional, because it's may either none or a reference. – Alexander Chen Jun 02 '20 at 06:49
  • @AlexanderChen: If we're "in modern C++ thoughts", then a `T*` should always represent non-ownership. If you wanted to represent a transfer of ownership, the way to do that in modern C++ is to use a smart pointer of some kind. So the only time a caller should think that is if he's dealing with code that isn't written in a modern C++ style. – Nicol Bolas Jun 02 '20 at 15:01
  • 2
    Why not just disable the assignment opertor for std::optional? std::optional should be designed to be constructed and passed to function or returned from a function. Not for reassigning it's value, which leads to such problems. – Nuclear Apr 29 '21 at 07:51
  • @Nuclear: Why should we give up useful functionality like assignment of an `optional`? For non-reference types, what it does makes sense and is well understood. There's no reason to *only* use `optional` for communicating to/from a function. – Nicol Bolas Apr 29 '21 at 13:19
5

In [optional]:

A program that necessitates the instantiation of template optional for a reference type, or for possibly cv-qualified types in_place_t or nullopt_t is ill-formed.

There is no std::optional<T&>. For now, you'll have to use std::optional<std::reference_wrapper<T>>.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • will this support `->`? The `std::reference_wrapper` doesn't seem to implement `operator->`. So `o->foo` will not work. – Johannes Schaub - litb Jul 18 '17 at 15:17
  • 1
    @JohannesSchaub-litb You'd need two indirections - like `o->get().foo`, cause `->` just gives you the `reference_wrapper`. I do hope that eventually we get `optional` that rebinds on assignment, I've become convinced by many people that that's the right behavior. – Barry Jul 18 '17 at 15:23
  • @JohannesSchaub-litb I came to the same conclusion, so I wrote [a wrapper class](https://wandbox.org/permlink/u6TOdDuOKrbK5Tue), and ended up here to see if anyone had any similar ideas. This translates `std::optional` into `std::optional>`, but it automatically unwraps the reference on `operator->` and `operator*`. `operator=` will rebind the reference. – monkey0506 Aug 23 '19 at 02:35