Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does make_optional decay its argument type?

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?

like image 674
ecatmur Avatar asked Jul 03 '14 14:07

ecatmur


3 Answers

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.

like image 158
Yakk - Adam Nevraumont Avatar answered Oct 23 '22 15:10

Yakk - Adam Nevraumont


This is not new behaviour with make_optional; the utility functions make_pair and make_tuple behave in the same way. I can see at least two reasons to do this:

  1. It may be undesirable or impossible to actually instantiate the template with the undecayed types.

    • If T is a function type, well, you just can't store a function inside a class, period; but you can store a function pointer (the decayed type).

    • If T is an array type: the resulting class is going to be "defective" because it can't be copied, owing to the fact that arrays are not copy-assignable. For optional the value_or member function can't be compiled at all, because it returns T. Consequently you can't have an array type in an optional at all.

  2. Not decaying the types may lead to unexpected behaviour.

    • If the argument is a string literal, I personally would expect the type contained to be const char* rather than const char [n]. This decay occurs in most places, so why not here?

    • If v is an lvalue then T will be deduced as an lvalue reference type. Do I actually want a pair containing a reference, for instance, just because one of the arguments was an lvalue? Surely not.

    • The type inside the pair or tuple or optional or whatever shouldn't acquire the cv-qualification of v. That is, say we have x declared as const int. Should make_optional(x) create a optional<const int>? No it shouldn't; it should create optional<int>.

like image 22
Brian Bi Avatar answered Oct 23 '22 15:10

Brian Bi


Just look at what decay does:

  • Removes reference qualifiers—T&T; no optional references, else you couldn’t reseat the optional, which needs to be assignable.

  • For array types, removes extent—T[42]T*; optional fixed-extent arrays aren’t that useful because each array size has a different type, and you can’t directly pass array types by value, necessary for the value and value_or member functions to work.

  • For function types, adds pointer—T(U)T(*)(U); no optional function references, for similar reasons.

  • Otherwise, removes cv-qualifiers—const intint; no optional const values, again, else you couldn’t reseat the optional.

like image 7
Jon Purdy Avatar answered Oct 23 '22 15:10

Jon Purdy