Specialization std :: optional

Is it possible to specialize std::optional for custom types? If not, is it too late to offer this to the standard?

My use case for this is an integer class that represents a value within a range. For example, you might have an integer that is somewhere in the range [0, 10]. Many of my applications are sensitive to even one byte of overhead, so I would not be able to use the non-specialized std::optional due to the extra bool . However, specializing for std::optional would be trivial for an integer that has a range smaller than its base type. We could just keep the value 11 in my example. This should not take up extra space or time over an optional value.

Am I allowed to create this specialization in namespace std ?

+8
c ++ std c ++ 14 template-specialization
source share
5 answers

The general rule in 17.6.4.2.1 [namespace.std] / 1 applies:

The program can add the template specialization for any standard library template to the std only if the declaration depends on the user type, and the specialization meets the requirements of the standard library for the original template and is not explicitly prohibited.

Therefore, I would say that this is allowed.

NB optional will not be part of the C ++ 14 standard, it will be included in a separate technical specification on the basics of the library, so it's time to change the rule if my interpretation is incorrect.

+8
source share

If you are after a library that effectively packs the value and the "no-value" flag into one memory location, I recommend looking at compact_optional . He does just that.

It does not specialize in boost::optional or std::experimental::optional , but can wrap them inside, providing you with a single interface with optimization where possible, and a backup of the "classic" if necessary.

+5
source share

I asked about the same, regarding the specialization optional<bool> and optional<tribool> among other examples, in order to use only one byte. Although the "legitimacy" of such actions has not been discussed, I think that theoretically one should not specialize in optional<T> as opposed to: hash (which is explicitly allowed).

I donโ€™t have any magazines with me, but part of the rationale is that the interface considers access to data as access to a pointer or a link, which means that if you use a different data structure in internal components, some of these access invariants may change; not to mention that a data access interface might require something like reinterpret_cast<(some_reference_type)> . For example, using uint8_t to store optional-bool imposes several additional requirements on the optional<bool> interface, which differ from the optional<T> tags. What should be the return type of operator* , for example?

Basically, I guess the idea is to avoid the vector<bool> fiasco again.

In your example, this might not be so bad, since the access type is still your_integer_type& (or the pointer). But in this case, just create your integer type to use a "zombie" or "indefinite" value instead of relying on optional<> to do the job for you, with its additional overhead and requirements, may be the safest choice .

+2
source share

I donโ€™t see how to allow or not to allow a particular bit pattern to represent an idle state that fits all standard shells.

If you tried to convince the library vendor to do this, it would require an implementation, exhaustive tests to show that you did not accidentally explode any requirements for optional (or accidentally called undefined behavior) and extensive benchmarking to show this, a noticeable difference in the real ( and not just far-fetched) situations.

Of course, you can do whatever you want for your own code.

0
source share

Simplify space saving

I decided that this was a useful thing, but a full specialization is a little more work than necessary (for example, getting operator= correct).

I posted on the Boost mailing list a way to simplify the specialization task, especially when you want to specialize in some instances of a class template.

http://boost.2283326.n4.nabble.com/optional-Specializing-optional-to-save-space-td4680362.html

My current interface includes a special type of tag used to "unlock" access to certain functions. I creatively named this type optional_tag . Only optional can build optional_tag . To select a type in a space-efficient representation, it needs the following member functions:

  • T(optional_tag) builds an uninitialized value
  • initialize(optional_tag, Args && ...) creates an object if it already exists
  • uninitialize(optional_tag) destroys the contained object
  • is_initialized(optional_tag) checks if an object is in its current state

By always requiring the optional_tag parameter, we do not limit any function signatures. That is why, for example, we cannot use operator bool() as a test, because the type may require this operator for other reasons.

The advantage of this over some other possible ways to implement it is that you can make it work with any type that can naturally support this state. It does not add any requirements, such as the presence of a move constructor.

You can see the full implementation of the code in

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 specialization:

https://bitbucket.org/davidstone/bounded_integer/src/8c5e7567f0d8b3a04cc98142060a020b58b2a00f/bounded_integer/detail/class.hpp?at=default&fileviewer=file-view-default

(lines 220 to 242)

Alternative approach

This is different from my previous implementation, which requires users to specialize the 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 for the user. Instead of adding four member functions, the user must move to a new namespace and specialize in the template.

In practice, all specializations will have an in_place_t constructor, which forwards all arguments to the base type. On the other hand, the optional_tag approach can directly use base type constructors.

In the optional_storage special approach, the user is also responsible for adding the appropriate reference overloads of the value function. In the optional_tag approach, we already have a value, so we donโ€™t need to pull it out.

optional_storage also required standardization as part of the interface of the optional two auxiliary classes, only one of which the user should specialize (and sometimes delegate his specialization to the other).

Difference between this and compact_optional

compact_optional is a way of saying, "Treat this special sentinel as an absent type, almost like NaN." This requires the user to know that the type they are working with has some kind of special guardian. An easily specialized optional is a way of saying: "My type does not need additional space to hold the current state, but this state is not a normal value." This does not require anyone to know about optimization in order to take advantage of it; everyone who uses this type gets it for free.

Future

My goal is to port this to boost :: optional, and then part of the std :: optional clause. Until then, you can always use bounded::optional , although it has several other (intentional) differences in the interface.

0
source share

All Articles