Может ли std :: byte заменить std :: align_storage? - PullRequest
1 голос
/ 08 октября 2019

C ++ 17 ввел новый тип, std::byte, так что теперь у нас наконец есть первоклассный гражданский тип для представления байтов в памяти. Помимо новизны в стандарте, правила C ++ для создания объектов, начала и конца жизни, создания псевдонимов и т. Д. В большинстве случаев довольно сложны и не интуитивны, поэтому всякий раз, когда я чувствую, что std::byte является правильным инструментом, я также нервничаю инеохотно использует его, опасаясь непреднамеренного вызова неопределенных поведенческих шаров.

Одним из таких случаев является буфер, который будет использоваться с новым размещением:

#include <memory>
#include <cstddef>
#include <type_traits>

struct X { double dummy[4]; char c; };

auto t1()
{
    // the old way

    std::aligned_storage_t<sizeof(X)> buffer;
    X* x = new (&buffer) X{};

    x->~X();
}

auto t2()
{
    // the new way?

    std::byte buffer[sizeof(X)];
    X* x = new (&buffer) X{};

    x->~X();
}

Является ли t2 совершенно безопасным иэквивалентно t1?

В ответ на проблемы с выравниванием, как насчет:

auto t3()
{
    alignas(X) std::byte buffer[sizeof(X)];

    X* x = new (&buffer) X{};
    x->~X();
}

Ответы [ 2 ]

3 голосов
/ 08 октября 2019

Является ли t2 совершенно безопасным и эквивалентным t1?

Нет. std::aligned_storage создает хранилище, которое выровнено соответствующим образом для помещаемого в него объекта. std::byte buffer[sizeof(X)], будучи правильным размером, имеет выравнивание std::byte. Это обычно не будет иметь такое же выравнивание типа, которое вы помещаете в него, так как оно имеет выравнивание 1.

Это не проблема, если речь идет о виртуальной машине C ++, но в реальнойЭто может привести к серьезным потерям производительности или даже к аварийному завершению программы.

Если вы хотите правильно выровнять хранилище, используйте std::aligned_storage см. Ответ Барри

2 голосов
/ 08 октября 2019

Является ли t2 совершенно безопасным и эквивалентным t1?

Нет. На самом деле, оба являются плохими.

t2 - это плохо по той причине, что NathanOliver указывает: он не выровнен. Вам нужно было бы написать:

alignas(X) std::byte storage[sizeof(X)];

t1 также эта проблема связана с тем, что вы почти наверняка захотите написать aligned_storage_t<sizeof(X), alignof(X)>, а не просто aligned_storage_t<sizeof(X)>. Если бы X было переопределено, вы бы потеряли это здесь. Если бы X был просто большим, но не требовал выравнивания, у вас получилось бы относительно многоуровневое хранилище.

t1 также плохо по особой причине: aligned_storage не совсем гарантирует, чтоты думаешь это гарантирует. В частности, он гарантирует, что X может соответствовать aligned_storage<sizeof(X)>, но не гарантирует, что он может точно соответствовать . Спецификация просто:

Элемент typedef type должен быть тривиальным типом макета стандарта, подходящим для использования в качестве неинициализированного хранилища для любого объекта, размер которого не превышает * 1034. * и выравнивание которого является делителем Align.

То есть aligned_storage<16>::type гарантированно будет иметь размер не менее 16 байтов, но соответствующая реализация может легко дать вам 32. Или 4K. Это в дополнение к проблеме случайного использования aligned_storage<16> вместо aligned_storage_t<16>.

Вот почему P1413 существует как бумага: aligned_storage - это плохо.

Таким образом, реальный ответ на самом деле - написать что-то вроде __aligned_membuf:

template <typename T>
struct storage_for {
    alignas(T) std::byte data[sizeof(T)];

    // some useful constructors and stuff, probably some getter
    // that gives you a T* or a T const*
};
в libstdc ++
...