Why does make_optional decay its argument type?

c++, c++14, option-type, std, type-traits

Solution

A general purpose of `decay` is to take a type and modify it to be suitable for storage.

Take a look at these examples that `decay` makes work, while `remove_reference` would not:

auto foo( std::string const& s ) {
  if (global_condition)
    return make_optional( s );
  else
    return {};
}

or

void function() { std::cout << "hello world!\n"; }
auto bar() { return std::make_optional( function ); }

or

int buff[15];
auto baz() { return std::make_optional( buff ); }

An `optional<int[15]>` would be a very strange beast -- C style arrays do not behave well when treated like literals, which is what `optional` does to its parameter `T`.

If you are making a copy of data, the `const` or `volatile` nature of the source does not matter. And you can only make simple copy of arrays and functions by decaying them to pointers (without falling back on `std::array` or similar). (in theory, work could be done to make `optional<int[15]>` work, but it would be lots of extra complications)

So `std::decay` solves all of these issues, and does not really cause problems, so long as you allow `make_optional` to deduce its argument's type instead of passing `T` literally.

If you want to pass in a `T` literally, there is no reason to use `make_optional` after all.

Problem

The (probably not C++14, probably Library TS) facility `make_optional` is defined (in n3672) as: ``` template <class T> constexpr optional<typename decay<T>::type> make_optional(T&& v) { return optional<typename decay<T>::type>(std::forward<T>(v)); } ``` Why is it necessary to transform the type `T` (i.e. not to just return `optional<T>`), and is there a philosophical (as well as practical) justification for using `decay` specifically as the transformation?

Original source