[Development] Optional (was RE: Improving toInt, toLong, etc.)

Tony Van Eerd tvaneerd at blackberry.com
Wed Feb 5 19:49:43 CET 2014


I've followed std::optional closely through the standardization process, so I can give some background info:

- from the beginning Boost::optional was written with a pointer-like syntax:

	optional<int> op = maybe_get_the_value();

	if (op) {
		int x = *op;
		...
	}

The original Boost author felt *strongly* that a pointer-like API would be best, as developers are familiar with the paradigm.

- optional's constructor allows *implicit* conversion from T, for various reasons, including so that it is easy to change a function that takes a T into a function that takes an optional<T>.

// version 1:
int function(int); 
// version 2:
int function(optional<int>);

// my code:
int x = function(17); // works with both versions

- Assuming optional<T> == optional<T> exists, then implicit constructors means that things like optional<T> == T automatically work (T is implicitly converted to optional<T>)

so optional<bool>(false) == bool(false) can compile.  But that has the confusion with if (op).  Which is done as an _explicit_ bool  conversion.

Once you have == and !=, the next question is <,>,<=,>=.  It is easy to say we don't want/need these.  HOWEVER...

We want operator<() so that optionals can be placed into ordered containers, be sorted, etc.  More precisely, we want std::less to work.  But if std::less works, shouldn't operator<() as well (and shouldn't they give the same results!)

...  note that there are some rare cases that std::less works, but operator<() doesn't - ie pointers (in general).  Also, sooner or later, probably std::complex will be similar.  (Currently std::complex can't be used as a key in map, giving it std::less would fix that.  But we don't want complex to have operator<() as it doesn't really make sense...)


Overall, optional can be seen in many ways:

1.    Just a T with deferred initialization / lifetime management.
2.    A discriminated union of types nullopt_t and T.
3.    A container of T's with the maximum size of 1.
4.    A type "T+" which is the same as T, but with one more value

The interface of std::experimental::optional is most closely aligned with 1 and 4.  It definitely doesn't have much container interface (like begin/end/iterator/...).

1 is what optional really is, everything else is extending it to try to be more than that.
It is really 1 because "op = 17" does construction or assignment depending on whether the optional was already engaged or not.  If your class handles construction and assignment differently, this is an important distinction. (Of course most types are Regular so not an issue.)

Oh, and then there are (or are not) optional<T&> types to consider.  The standard is avoiding those for now. (Note that T& is not Regular, and that for references construction and assignment of are different!)


And yes, these issues are why it is in a TS instead of C++14.  

Tony




More information about the Development mailing list