Могу ли я вернуть опцию из функции constexpr?

  • Можно ли вернуть optional из функции constexpr?
  • Почему?
  • Если да, как это работает?

Меня интересуют как boost::optional, так и std::optional. Они ведут себя одинаково?

Ответ 1

boost::optional не может быть возвращена функцией constexpr. По крайней мере, документация не дает никаких гарантий.

Однако std::optional, как определено принятым предложением С++ 14, может быть возвращено функцией constexpr. Но только, если аргумент типа optional тривиально разрушен.

Это позволяет деструктору std::optional быть тривиальным в этих случаях. В этот момент нет никаких трудностей в уничтожении объекта, поэтому нет ничего, что останавливало бы std::optional от буквального типа.

Это предложение совершенно очевидно. Если T тривиально разрушается, то большинство конструкторов optional будет constexpr, а optional<T> будет буквальным. И поэтому он может быть создан в функции constexpr.

Ответ 2

Boost.Optional не поддерживает constexpr, главным образом потому, что он был написан до того, как был выпущен С++ 11.

Текущее предложение для std::optional поддерживает constexpr, если тип значения T является тривиально разрушаемым. Он работает, потому что для союзов допустимы конструкторы constexpr (7.1.5p4); компилятор отслеживает, какой член объединения инициализирован, гарантируя, что поведение undefined доступа к значению освобожденного необязательного значения происходит во время компиляции:

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;
};

Тип значения должен быть тривиально разрушаемым, потому что constexpr полезен только для литералов, которые сами должны иметь тривиальный деструктор.

Например, запись:

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

gcc дает ошибку:

error: accessing ‘optional_storage<int>::value_’ member instead of initialized 
‘optional_storage<int>::dummy_’ member in constant expression