std:: необязательный, реализованный как union против char[]/align_storage
Читая через реализацию GCC std::optional
Я заметил кое-что интересное. я знаю boost::optional
реализован следующим образом:
template <typename T>
class optional {
// ...
private:
bool has_value_;
aligned_storage<T, /* ... */> storage_;
}
Но тогда и libstdC++, и libC++ (и Abseil) реализуют свои optional
типа как это:
template <typename T>
class optional {
// ...
private:
struct empty_byte {};
union {
empty_byte empty_;
T value_;
};
bool has_value_;
}
Они выглядят для меня так же, как они функционально идентичны, но есть ли преимущества использования одного над другим? (За исключением очевидного отсутствия размещения нового в последнем, что действительно приятно.)
2 ответа
Они выглядят для меня так же, как они функционально идентичны, но есть ли преимущества использования одного над другим? (За исключением очевидного недостатка размещения нового в последнем, что действительно приятно.)
Это не просто "очень приятно" - это важно для действительно важной функциональности, а именно:
constexpr std::optional<int> o(42);
Есть несколько вещей, которые вы не можете сделать в постоянном выражении, и они включают new
а также reinterpret_cast
, Если вы реализовали optional
с aligned_storage
, вам нужно будет использовать new
создать объект и reinterpret_cast
чтобы вернуть его обратно, что помешало бы optional
из того constexpr
дружелюбный.
С union
реализации, у вас нет этой проблемы, поэтому вы можете использовать optional
в constexpr
программирование (даже до исправления тривиального копирования, о котором говорит Николь, optional
уже был необходим для использования в качестве constexpr
).
std::optional
не может быть реализовано как выровненное хранилище из-за исправления после C++17. В частности, std::optional<T>
должно быть легко копируемым, если T
тривиально копируемый. union{empty; T t};
удовлетворит это требование
Внутреннее хранение и размещениеnew
/delete
использование не может. Создание байтовой копии из объекта TriviallyCopyable в хранилище, в котором еще нет объекта, в модели памяти C++ недостаточно для фактического создания этого объекта. Напротив, сгенерированная компилятором копия занятого union
над TriviallyCopyable типов будет тривиальным и будет работать для создания целевого объекта.
Так std::optional
должен быть реализован таким образом.