Will it be possible to specialize std::optional
for user-defined types? If not, is it too late to propose this to the standard?
My use case for this is an integer-like class that represents a value within a range. For instance, you could have an integer that lies somewhere in the range [0, 10]. Many of my applications are sensitive to even a single byte of overhead, so I would be unable to use a non-specialized std::optional
due to the extra bool
. However, a specialization for std::optional
would be trivial for an integer that has a range smaller than its underlying type. We could simply store the value 11
in my example. This should provide no space or time overhead over a non-optional value.
Am I allowed to create this specialization in namespace std
?
I have decided that this is a useful thing to do, but a full specialization is a little more work than necessary (for instance, getting operator=
correct).
I have posted on the Boost mailing list a way to simplify the task of specializing, especially when you only want to specialize some instantiations of a class template.
http://boost.2283326.n4.nabble.com/optional-Specializing-optional-to-save-space-td4680362.html
My current interface involves a special tag type used to 'unlock' access to particular functions. I have creatively named this type optional_tag
. Only optional
can construct an optional_tag
. For a type to opt-in to a space-efficient representation, it needs the following member functions:
T(optional_tag)
constructs an uninitialized valueinitialize(optional_tag, Args && ...)
constructs an object when there may be one in existence alreadyuninitialize(optional_tag)
destroys the contained objectis_initialized(optional_tag)
checks whether the object is currently in an initialized stateBy always requiring the optional_tag parameter, we do not limit any function signatures. This is why, for instance, we cannot use operator bool()
as the test, because the type may want that operator for other reasons.
An advantage of this over some other possible methods of implementing it is that you can make it work with any type that can naturally support such a state. It does not add any requirements such as having a move constructor.
You can see a full code implementation of the idea at
https://bitbucket.org/davidstone/bounded_integer/src/8c5e7567f0d8b3a04cc98142060a020b58b2a00f/bounded_integer/detail/optional/optional.hpp?at=default&fileviewer=file-view-default
and for a class using the specialization:
https://bitbucket.org/davidstone/bounded_integer/src/8c5e7567f0d8b3a04cc98142060a020b58b2a00f/bounded_integer/detail/class.hpp?at=default&fileviewer=file-view-default
(lines 220 through 242)
This is in contrast to my previous implementation, which required users to specialize a class template. You can see the old version here:
https://bitbucket.org/davidstone/bounded_integer/src/2defec41add2079ba023c2c6d118ed8a274423c8/bounded_integer/detail/optional/optional.hpp
and
https://bitbucket.org/davidstone/bounded_integer/src/2defec41add2079ba023c2c6d118ed8a274423c8/bounded_integer/detail/optional/specialization.hpp
The problem with this approach is that it is simply more work for the user. Rather than adding four member functions, the user must go into a new namespace and specialize a template.
In practice, all specializations would have an in_place_t
constructor that forwards all arguments to the underlying type. The optional_tag
approach, on the other hand, can just use the underlying type's constructors directly.
In the specialize optional_storage
approach, the user also has the responsibility of adding proper reference-qualified overloads of a value function. In the optional_tag
approach, we already have the value so we do not have to pull it out.
optional_storage
also required standardizing as part of the interface of optional two helper classes, only one of which the user is supposed to specialize (and sometimes delegate their specialization to the other).
compact_optional
is a way of saying "Treat this special sentinel value as the type being not present, almost like a NaN". It requires the user to know that the type they are working with has some special sentinel. An easily specializable optional
is a way of saying "My type does not need extra space to store the not present state, but that state is not a normal value." It does not require anyone to know about the optimization to take advantage of it; everyone who uses the type gets it for free.
My goal is to get this first into boost::optional, and then part of the std::optional proposal. Until then, you can always use bounded::optional
, although it has a few other (intentional) interface differences.
The general rule in 17.6.4.2.1 [namespace.std]/1 applies:
A program may add a template specialization for any standard library template to namespace
std
only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.
So I would say it's allowed.
N.B. optional
will not be part of the C++14 standard, it will be included in a separate Technical Specification on library fundamentals, so there is time to change the rule if my interpretation is wrong.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With