wake-up-neo.com

Welche Eigenschaft/welches Konzept kann sicherstellen, dass das Merken eines Objekts genau definiert ist?

Angenommen, ich habe eine Funktion zero_initialize() definiert:

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

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

Das Aufrufen von zero_initialize() für einige Typen würde zu undefiniertem Verhalten führen1,2. Ich erzwinge derzeit die Überprüfung von std::is_pod durch T. Angesichts der Tatsache, dass dieses Merkmal in C++ 20 veraltet ist und Konzepte aufkommen, bin ich gespannt, wie sich zero_initialize() entwickeln sollte.

  1. Welche (minimale) Eigenschaft/welches Konzept kann sicherstellen, dass das Merken eines Objekts genau definiert ist?
  2. Soll ich std::uninitialized_fill anstelle von std::memset verwenden? Und warum?
  3. Ist diese Funktion durch eine der C++ - Initialisierungssyntaxen für eine Teilmenge von Typen veraltet? Oder wird es mit den kommenden C++ Versionen sein?

1) Alle Mitglieder einer Klasse löschen .
2) Was wäre der Grund für "undefiniertes Verhalten" bei der Verwendung von Memset für Bibliotheksklassen (std :: string)? [geschlossen]

20
YSC

Es gibt technisch keine Objekteigenschaft in C++, die angibt, dass Benutzercode ein C++ - Objekt legal memset kann. Und dazu gehört auch POD. Wenn Sie also technisch sein wollen, war Ihr Code nie korrekt. Sogar TriviallyCopyable ist eine Eigenschaft, bei der byteweise Kopien zwischen vorhandenen Objekten (manchmal durch einen zwischenliegenden Byte-Puffer) erstellt werden. Es sagt nichts darüber aus, Daten zu erfinden und sie in die Bits des Objekts zu stecken.

Davon abgesehen, können Sie vernünftigsicher sein, dass dies funktioniert, wenn Sie is_trivially_copyableundis_trivially_default_constructible testen. Letzteres ist wichtig, da einige TriviallyCopyable-Typen immer noch in der Lage sein möchten, ihren Inhalt zu steuern. Ein solcher Typ könnte beispielsweise eine private Variable int haben, die immer 5 ist und im Standardkonstruktor initialisiert wird. Solange sich kein Code mit Zugriff auf die Variable ändert, ist es immer 5. Das C++ - Objektmodell garantiert dies.

Sie können also ein solches Objekt nicht memset und erhalten dennoch ein genau definiertes Verhalten aus dem Objektmodell.

23
Nicol Bolas

Welches (minimale) Merkmal/Konzept kann garantieren, dass ein Objekt gut definiert ist?

Gemäß der std::memset-Referenz auf cppreference ist das Verhalten von memset bei einem Typ, der nicht TriviallyCopyable ist, undefiniert. Wenn es also in Ordnung ist, memset a TriviallyCopyable zu verwenden, können Sie Ihrer Klasse einen static_assert hinzufügen, um dies zu überprüfen

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;
}

Hier verwenden wir std::is_trivial_v , um sicherzustellen, dass nicht nur die Klasse trivial kopierbar ist, sondern auch ein trivialer Standardkonstruktor vorhanden ist.

Sollte ich std::uninitialized_fill anstelle von std::memset verwenden? Und warum?

Sie brauchen nicht hier zu sein, da Sie nur ein einzelnes Objekt initialisieren.

Ist diese Funktion durch eine der C++ - Initialisierungssyntaxen für eine Teilmenge von Typen überflüssig? Oder wird es mit dem Erscheinen zukünftiger C++ - Versionen geschehen?

Die wert- oder geschweifte Initialisierung macht diese Funktion "obsolet". T() und T{} geben Ihnen einen initialisierten Wert T, und wenn T keinen Standardkonstruktor hat, wird dieser mit Null initialisiert. Das heißt, Sie könnten die Funktion als neu schreiben

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

Die allgemein definierbarste Eigenschaft, die garantiert, dass zero_initialize Objekte tatsächlich nullinitialisiert, ist

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

Nicht zu nützlich Die einzige Garantie für bitweise oder byteweise Darstellungen grundlegender Typen im Standard ist jedoch [basic.fundamental]/7 "Die Repräsentationen von ganzzahligen Typen definieren Werte unter Verwendung eines reinen binären Zahlensystems." Es gibt keine Garantie dafür, dass ein Gleitkommawert mit allen Bytes Null ein Nullwert ist. Es gibt keine Garantie, dass ein Zeiger oder Zeiger-zu-Member-Wert mit allen Bytes Null ein Nullzeigerwert ist. (Obwohl beide in der Praxis normalerweise zutreffen.)

Wenn alle nicht statischen Member eines trivial kopierbaren Klassentyps (Arrays von) (cv-qualifizierten) Integraltypen sind, denke ich, dass dies ebenfalls in Ordnung wäre, aber es gibt keine Möglichkeit, dies zu testen, es sei denn, die Überlegungen zu C++.

0
aschepler