11
  • Can I return an optional from a constexpr function?
  • Why?
  • If yes, how does it work?

I'm interested in both boost::optional and std::optional. Do they behave the same?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
gnzlbg
  • 7,135
  • 5
  • 53
  • 106
  • 1
    with `std::optional` surely you [forgot about the c++1y tag](http://isocpp.org/blog/2013/04/trip-report-iso-c-spring-2013-meeting)? – sehe May 07 '13 at 13:17
  • @sehe thanks for the edit! What is the difference between `std::optional` and `boost::optional` that allows this? Will it ever be possible in `boost::optional` for c++11 ? – gnzlbg May 07 '13 at 13:19

2 Answers2

11

boost::optional cannot be returned by a constexpr function. Or at least, the documentation provides no guarantees of this.

However, std::optional, as defined by the accepted C++14 proposal, can be returned by a constexpr function. But only if the type argument of the optional is trivially destructible.

This allows the destructor of std::optional to be trivial in those cases. At which point, there's no difficulty in destroying the object, so there's nothing stopping std::optional from being a literal type.

The proposal is quite clear on this. If T is trivially destructible, then most of the constructors of optional will be constexpr, and optional<T> will be a literal type. And therefore, it can be created in a constexpr function.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
6

Boost.Optional does not support constexpr, mainly because it was written before C++11 was released.

The current proposal for std::optional does support constexpr, as long as the value type T is trivially destructible. It works because constexpr constructors are allowed for unions (7.1.5p4); the compiler tracks which union member is initialized, ensuring that the undefined behaviour of accessing the value of a disengaged optional is caught at compile time:

struct dummy_t {};
template <class T>
union optional_storage {
  static_assert( is_trivially_destructible<T>::value, "" );
  dummy_t dummy_;
  T       value_;
  constexpr optional_storage(): dummy_{} {}  // disengaged
  constexpr optional_storage(T const& v): value_{v} {}  // engaged
  ~optional_storage() = default;
};

The value type must be trivially destructible because constexpr is only useful with literal types, which must themselves have a trivial destructor.

For example, writing:

constexpr optional_storage<int> o{};
constexpr int i = o.value_;

gcc gives the error:

error: accessing ‘optional_storage<int>::value_’ member instead of initialized 
‘optional_storage<int>::dummy_’ member in constant expression
ecatmur
  • 152,476
  • 27
  • 293
  • 366