Какая черта / концепция может гарантировать, что memsetting объект хорошо определен? - PullRequest
0 голосов
/ 16 ноября 2018

Допустим, я определил функцию zero_initialize():

template<class T>
T zero_initialize()
{
    T result;
    std::memset(&result, 0, sizeof(result));
    return result;
}

// usage: auto data = zero_initialize<Data>();

Вызов zero_initialize() для некоторых типов приведет к неопределенному поведению 1, 2 ,В настоящее время я заставляю T проверить std::is_pod.С учетом того, что эта черта устарела в C ++ 20 и появление концепций, мне любопытно, как zero_initialize() должен развиваться.

  1. Какая (минимальная) черта / концепция может гарантировать, что memsetting объект хорошоопределены?
  2. Должен ли я использовать std::uninitialized_fill вместо std::memset?И почему?
  3. Эта функция устарела одним из синтаксисов инициализации C ++ для подмножества типов?Или это будет с будущими версиями C ++?

1) Удалить всех членов класса .
2) Какая причина для «неопределенного поведения» при использовании memset в классе библиотеки (std :: string)?[Закрыто]

Ответы [ 3 ]

0 голосов
/ 16 ноября 2018

Самая общая определяемая черта, которая гарантирует, что ваши zero_initialize будут фактически инициализировать объекты с нуля, это

template <typename T>
struct can_zero_initialize :
    std::bool_constant<std::is_integral_v<
        std::remove_cv_t<std::remove_all_extents_t<T>>>> {};

Не слишком полезно.Но единственной гарантией о побитовых или побитовых представлениях фундаментальных типов в Стандарте является [basic.fundamental] / 7 «Представления целочисленных типов должны определять значения с использованием чисто двоичной системы счисления».Не гарантируется, что значение с плавающей точкой со всеми байтами, равными нулю, является нулевым значением.Не гарантируется, что любое значение указателя или указателя на элемент со всеми байтами, равными нулю, является нулевым значением указателя.(Хотя оба они обычно выполняются на практике.)

Если все нестатические члены тривиально копируемого типа класса являются (массивами) (cv-квалифицированными) целочисленными типами, я думаю, что это также будетхорошо, но нет никакого способа проверить это, если только рефлексия не придет в C ++.

0 голосов
/ 16 ноября 2018

Технически нет свойства объекта в C ++, которое указывает, что пользовательский код может легально memset объект C ++. И это включает POD, поэтому, если вы хотите быть техническим, ваш код никогда не был правильным. Даже TriviallyCopyable - это свойство делать побайтные копии между существующими объектами (иногда через промежуточный байтовый буфер); в нем ничего не говорится о том, чтобы придумывать данные и разбивать их на куски объекта.

При этом вы можете быть разумно уверены, что это сработает, если вы протестируете is_trivially_copyable и is_trivially_default_constructible. Последнее важно, потому что некоторые типы TriviallyCopyable по-прежнему хотят иметь возможность контролировать их содержимое. Например, такой тип может иметь закрытую переменную int, которая всегда равна 5, инициализирован в конструкторе по умолчанию. Пока никакой код с доступом к переменной не изменит его, он всегда будет равен 5. Объектная модель C ++ гарантирует это.

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

0 голосов
/ 16 ноября 2018

Какая (минимальная) черта / концепция может гарантировать правильную настройку объекта?

В соответствии со ссылкой std::memset на cppreference поведение memset в типе, отличном от TriviallyCopyable , не определено. Так что если все в порядке с memset a TriviallyCopyable , тогда вы можете добавить static_assert в свой класс, чтобы проверить это как

template<class T>
T zero_initialize()
{
    static_assert(std::is_trivial_v<T>, "Error: T must be TriviallyCopyable");
    T result;
    std::memset(&result, 0, sizeof(result));
    return result;
}

Здесь мы используем std::is_trivial_v, чтобы удостовериться, что класс не только тривиально копируемый, но также имеет тривиальный конструктор по умолчанию, поэтому мы знаем, что инициализировать нулем безопасно.

Должен ли я использовать std::uninitialized_fill вместо std::memset? И почему?

Вам не нужно здесь, поскольку вы инициализируете только один объект.

Эта функция устарела одним из синтаксисов инициализации C ++ для подмножества типов? Или это будет с выходом будущих версий C ++?

Значение или ограниченная инициализация делают эту функцию "устаревшей". T() и T{} дадут вам инициализированное значение T, а если T не имеет конструктора по умолчанию, он будет инициализирован нулем. Это означает, что вы можете переписать функцию как

template<class T>
T zero_initialize()
{
    static_assert(std::is_trivial_v<T>, "Error: T must be TriviallyCopyable");
    return {};
}
...