2016-09-08 5 views
8

Die erste Klasse wird für private Vererbung verwendet, um das exakt gleiche Layout zu gewährleisten. Dies sollte Casting sicher machen.Ist es sicher, in eine Klasse zu konvertieren, die dasselbe Datenelementlayout aufweist, aber eine andere Implementierung?

#include <iostream> 
#include <string> 

struct data_base 
{ 
    data_base(int i, std::string&& s) noexcept 
     : i_{ i } 
     , s_{ std::move(s) } 
    {} 

    int i_; 
    std::string s_; 
}; 

In diesem trivialen Beispiel drucke ich das int Datenelement zuerst durch das std::string Daten Mitglied gefolgt von Instanzen von data<true>.

template<bool = true> 
struct data : private data_base // inherits 
{ 
    data(int i, std::string&& s) noexcept 
     : data_base(i, std::move(s)) 
    {} 

    void print() 
    { 
     std::cout << "data<true> - " << i_ << s_ << '\n'; 
    } 
}; 

jedoch die data<false> druckt das std::string Datenelement zunächst durch das int Daten Mitglied gefolgt.

template<> 
struct data<false> : private data_base 
{ 
    void print() 
    { 
     std::cout << "data<false> - " << s_ << i_ << '\n'; 
    } 
}; 

Beispiel:

int main() 
{ 
    data<true> d{ 5, "abc" }; 
    d.print(); 
    ((data<false>&)d).print(); 
} 

Demo: http://coliru.stacked-crooked.com/a/8b1262afe23dc0a2

Als die Demo zeigt, auch mit der -fstrict-aliasing Fahne, es gibt keine Warnungen ist.

Jetzt, da sie das gleiche Layout haben, dachte ich, dass ich einfach zwischen den beiden Typen umwandeln könnte, um eine andere Art von statischem Polymorphismus zu bekommen; ohne die Kosten eines virtuellen Funktionsaufrufs.

Ist diese Verwendung sicher oder löst ich ein undefiniertes Verhalten aus?

+0

Nicht sicher, ob 'std :: string' garantiert eine Standardlayout-Klasse ist –

+0

Ich denke, der 'print' Aufruf zählt als" Verwendung des gespeicherten Wertes "für die Zwecke der strengen Aliasing-Regel, aber nicht 100% sicher –

+2

Jede Umwandlung zwischen nicht verwandten Typen verursacht genaugenommen undefiniertes Verhalten. Außerdem wird in C++ empfohlen, die C++ - Cast-Operatoren anstelle von C-artigen Umwandlungen zu verwenden. – antred

Antwort

1

Aus [expr.reininterpret.cast]/11 in der Sprachspezifikation können Sie einen Verweis von einem Typ auf einen anderen umwandeln (wenn Sie einen Zeiger auf einen anderen umwandeln können).

Mit Ihren Klassenlayouts haben beide Typen eine gemeinsame Basisklasse, die alle Daten enthält. Die beiden abgeleiteten Typen fügen keine Datenelemente hinzu und fügen auch keine virtuellen Funktionen hinzu, sodass das Objektlayout für beide Klassen identisch ist.

So ist die Verwendung sicher, wenn Sie reinterpret_cast verwenden.

In diesem Fall ähnelt das dem Umwandeln in eine Referenz der Basisklasse und dem anschließenden Umsetzen dieser Referenz auf die andere abgeleitete Klasse.

+1

Meiner Meinung nach sagt [expr.reininterpret.cast]/11 lediglich, dass die Besetzung zwar durchgeführt werden kann, aber nicht klar ist ob das Ergebnis der Besetzung zu einem definierten Verhalten führt oder nicht. Als Daten sehen ist keine Daten , meine Erwartung ist, dass es undefiniert wäre. – antred

+1

@antred Im Allgemeinen wäre das Ergebnis der Besetzung undefiniert. Aber in diesem speziellen Fall, über den das OP nachfragt, da 'data ' und 'data ' eine gemeinsame Basisklasse ohne virtuelle Funktionen teilen und keine Klasse irgendwelche Daten oder virtuellen Funktionen hinzufügt, sind die Speicherlayouts beider Klassen identisch. – 1201ProgramAlarm

1

Es ist mehr oder weniger, was beschrieben here, die so genannte Boost-Mutante Idiom.

Es wird gesagt, dass (Hervorhebung von mir):

Boost-Mutante Idiom Verwendung von reinterpret_cast und hängt stark von der Annahme macht, dass die Speicherlayouts von zwei verschiedenen Strukturen mit identischen Datenelemente (Art und Reihenfolge) austauschbar. Obwohl der C++ - Standard diese Eigenschaft nicht garantiert, erfüllen praktisch alle Compiler es. Darüber hinaus ist das Mutantenidiom Standard, wenn nur POD-Typen verwendet werden.


Hinweis: die Seite ist ziemlich veraltet, ich weiß nicht, ob die jüngsten Revisionen etwas über dieGarantien geändert oben erwähnt.

+0

Die Frage war nicht, ob das überhaupt funktioniert. Das ist ziemlich selbstverständlich. Jeder einigermaßen vernünftige Compiler wird dies ohne böse Überraschungen handhaben. Eine wahnsinnige Menge an existierendem Code, der brechen würde, wenn dies nicht der Fall wäre. Die Frage war, ob Abgüsse wie diese (und die Verwendung der Ergebnisse der Besetzung) undefiniertes Verhalten riskieren, und die Antwort ist ein klares JA. – antred

+0

Ok, jetzt, da ich deine Antwort noch einmal gelesen habe, liefert sie tatsächlich eine Antwort auf die Frage des OP, also ignoriere bitte meinen Kommentar. – antred

+0

@antred Kein Problem, Kritik ist immer willkommen. ;-) ... Beachten Sie, dass Sie Ihre Kommentare löschen können, wenn Sie möchten. – skypjack

Verwandte Themen