Disons que j'ai défini une fonction zero_initialize()
:
template<class T>
T zero_initialize()
{
T result;
std::memset(&result, 0, sizeof(result));
return result;
}
// usage: auto data = zero_initialize<Data>();
L'appel de zero_initialize()
pour certains types entraînerait un comportement indéfini1, 2. J'applique actuellement T
pour vérifier std::is_pod
. Ce trait étant obsolète en C++ 20 et l’apparition de concepts, je suis curieux de savoir comment zero_initialize()
devrait évoluer.
std::uninitialized_fill
au lieu de std::memset
? Et pourquoi?1) Effacer tous les membres d'une classe .
2) Quelle serait la raison pour les “comportements indéfinis” sur l'utilisation de memset sur la classe de bibliothèque (std :: string)? [fermé]
Techniquement, aucune propriété d'objet en C++ ne spécifie que le code utilisateur peut légalement memset
un objet C++. Et cela inclut POD, donc si vous voulez être technique, votre code n’a jamais été correct. Même TriviallyCopyable est une propriété permettant de copier des octets entre des objets existants (parfois via un tampon d'octets intermédiaire); cela ne dit rien d'inventer des données et de les insérer dans les bits de l'objet.
Ceci étant dit, vous pouvez être raisonnablement sûr que cela fonctionnera si vous testez is_trivially_copyable
etis_trivially_default_constructible
. Ce dernier point est important, car certains types TriviallyCopyable veulent toujours pouvoir contrôler leur contenu. Par exemple, un tel type pourrait avoir une variable privée int
toujours égale à 5, initialisée dans son constructeur par défaut. Tant qu'aucun code ayant accès à la variable ne la modifie, il restera toujours à 5. Le modèle d'objet C++ le garantit.
Donc, vous ne pouvez pas memset
un tel objet et malgré tout obtenir un comportement bien défini à partir du modèle d'objet.
Quel trait/concept (minimal) peut garantir que la création d'un objet est bien définie?
Selon la référence std::memset
sur cppreference , le comportement de memset
sur un type non TriviallyCopyable n'est pas défini. Donc, si vous pouvez utiliser memset
a TriviallyCopyable, vous pouvez ajouter un static_assert
à votre classe pour vérifier que
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;
}
Ici, nous utilisons std::is_trivial_v
pour nous assurer que non seulement la classe est trivialement copiable, mais qu’elle a également un constructeur trivial par défaut, nous savons donc qu’elle peut être initialisée à zéro.
Devrais-je utiliser
std::uninitialized_fill
au lieu destd::memset
? Et pourquoi?
Vous n’avez pas besoin d’être ici puisque vous n’initialisez qu’un seul objet.
Cette fonction est-elle rendue obsolète par l'une des syntaxes d'initialisation C++ pour un sous-ensemble de types? Ou sera-ce avec les prochaines versions de C++ à venir?
L’initialisation par valeur ou par hachage rend cette fonction "obsolète". T()
et T{}
vous donneront une valeur initialisée T
et si T
ne possède pas de constructeur par défaut, il sera initialisé à zéro. Cela signifie que vous pouvez réécrire la fonction en tant que
template<class T>
T zero_initialize()
{
static_assert(std::is_trivial_v<T>, "Error: T must be TriviallyCopyable");
return {};
}
Le trait définissable le plus général qui garantit que votre zero_initialize
initialisera les objets à zéro est
template <typename T>
struct can_zero_initialize :
std::bool_constant<std::is_integral_v<
std::remove_cv_t<std::remove_all_extents_t<T>>>> {};
Pas trop utile. Mais la seule garantie concernant la représentation bit/bit ou par octet des types fondamentaux dans la norme est [basic.fundamental]/7 "Les représentations des types intégraux doivent définir les valeurs à l'aide d'un système de numération binaire pur." Il n'y a aucune garantie qu'une valeur à virgule flottante avec tous les octets zéro soit une valeur zéro. Il n'y a aucune garantie que tout pointeur ou valeur de pointeur sur membre comportant tous les octets zéro soit une valeur de pointeur nulle. (Bien que les deux soient généralement vrais dans la pratique.)
Si tous les membres non statiques d'un type de classe trivialement copiable sont (des tableaux de) types intégraux (qualifiés de cv), je pense que ce serait également correct, mais il n'y a aucun moyen de tester cela, à moins que C++ ne fasse l'objet d'une réflexion.