Значение тривиального разрушения

В С++ 17 новый std::optional означает, что он будет тривиально разрушаемым, если T тривиально разрушается в [optional.object.dtor]:

~optional();
1 Эффекты: Если is_trivially_destructible_v<T> != true и *this содержит значение, называет val->T::~T().
2 Примечания: Если is_trivially_destructible_v<T> == true, то этот деструктор должен быть тривиальным деструктором.

Таким образом, этот потенциальный фрагмент реализации будет не соответствовать стандарту:

template <class T>
struct wrong_optional {
    union { T value; };
    bool on;

    ~wrong_optional() { if (on) { value.~T(); } }
};

Мой вопрос: в чем преимущество этого мандата? Предположительно, для тривиально разрушаемых типов компилятор может понять, что value.~T() является no-op и не испускает код для wrong_optional<T>::~wrong_optional().

Ответ 1

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

Ответ 2

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

  • Тип может быть тривиально скопируемым. И это делает тип подходящим для всех видов оптимизации. Внедрение стандартной библиотеки Visual Studio имеет количество оптимизаций для работы с такими типами.

  • Это законно не беспокоиться о вызове деструктора тривиально разрушаемых типов. Вы можете просто освободить место для хранения. Это своего рода низкоуровневая вещь, но она может иметь преимущества. Это часть того, что позволяет реализациям делать эти оптимизации выше.

  • Типы с тривиальными деструкторами могут быть буквальными типами и, таким образом, являются объектами, которые могут быть созданы и обработаны во время компиляции.

Интерфейс

optional<T> пытается как можно больше вмешаться в поведение T. Поэтому, если вы можете что-то сделать с T, тогда вы должны сделать то же самое с optional<T>. Если нет веской причины не делать этого.

Ответ 3

В этом Q/A одна конкретная причина, о которой еще не упоминалось и стоит упомянуть, такова: тип, который тривиально копируется и тривиально разрушаемый, позволяет передавать его в регистры в ABI (см. соглашения об объявлении Agner Fog). Это может существенно повлиять на сгенерированный код. Простая функция типа:

std::optional<int> get() { return {42}; }

Можете испустить следующий код, если тип не является тривиально копируемым/разрушаемым:

    mov     rax, rdi
    mov     DWORD PTR [rdi], 42
    mov     BYTE PTR [rdi+4], 1
    ret

Но может получиться только следующее:

    movabs  rax, 4294967338
    ret

Это определенно лучше.