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

Допустим, я определил функцию 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. В настоящее время я std::is_pod T для проверки std::is_pod. С учетом того, что эта черта устарела в C++ 20, и появления концепций, мне любопытно, как должен развиваться zero_initialize().

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

1)Стереть всех членов класса.
2)Что может быть причиной "неопределенного поведения" при использовании memset в классе библиотеки (std :: string)? [закрыто]

Ответ 1

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

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

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

Ответ 2

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

В соответствии со ссылкой std::memset на cppreference поведение memset для типа, не являющегося TriviallyCopyable, не определено. Так что, если это нормально для memset 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 {};
}

Ответ 3

Наиболее общая определяемая черта, которая гарантирует, что ваш 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-квалифицированными) целочисленными типами, я думаю, что это также будет хорошо, но нет никакого возможного способа проверить это, если только рефлексия не придет к [CN10 ].