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 должен быть реализован таким образом.

Другие вопросы по тегам