2017-08-14 5 views
4

Von einigen C-Legacy-Code bekomme ich eine Reihe von Konstanten wie int *. Im C++ Teil habe ich eine enum des zugrunde liegenden Typs int. Die Konvertierung zwischen enum und int auf einer einzigen Basis funktioniert. Eine Konvertierung zwischen int * und enum * ist jedoch nicht möglich. Siehe Codebeispiel unten.Wie konvertiert man zwischen int * und enum *?

Warum ist das und wie würde ich einen Zeiger auf einige int Werte in einen Zeiger auf int enums und umgekehrt konvertieren? Ich erwarte, dass es funktioniert, da die Einzelwert-Conversions funktionieren und die zugrunde liegenden Typen identisch sind. Ich habe über What happens if you static_cast invalid value to enum class? gelesen, konnte aber nicht feststellen, ob möglicherweise ungültige Werte eine Rolle spielen.

int i = 3; 
enum E : int; 
E e; 

e = static_cast<E>(i); // ok 
i = static_cast<int>(e); // ok 

int *j; 
E * f; 

j = static_cast<int *>(&i); // ok 
f = static_cast<E *>(&i); // 'static_cast': cannot convert from 'int *' to 'E *' 
j = static_cast<int *>(&e); // 'static_cast': cannot convert from 'E *' to 'int *' 

// now use j and f 
*j = *f; 
+0

Haben Sie 'reinterpret_cast' probiert? – VTT

+0

Während Sie die Konvertierung mit 'reinterpret_cast' durchführen können, ist dies selten sinnvoll. Sie können den resultierenden Zeiger nicht dereferenzieren, ohne undefiniertes Verhalten aufzurufen. – davmac

+0

@davmac Scheint so, als ob ich entweder sicherstellen sollte, dass ich strenge Aliasing-Regeln abstelle oder über das Kopieren nachdenke. Dank der Antwort und der Diskussion unter der Antwort verstehe ich es jetzt besser. – Trilarion

Antwort

3

Warum ist das so?

Vom Compiler Sicht int* und E* sind Zeiger von verschiedenen nicht-verwandten Arten, weshalb static_cast hier nicht anwendbar ist.

Wie würde ich einen Zeiger auf einige int-Werte in einen Zeiger auf int enums und umgekehrt konvertieren?

Sie könnten versuchen, reinterpret_cast statt static_cast:

f = reinterpret_cast<E *>(&i); 
j = reinterpret_cast<int *>(&e); 

Von reinterpret_cast:

Alle Zeiger vom Typ T1 zum Objekt kann umgewandelt werden, um Objekt-Zeiger eines anderen Typs cv T2

Beachten Sie jedoch, dass dereferenci ng f oder j (d.h. mit *f oder *j) wird eine Verletzung der strengen Aliasing-Regel sein (für weitere Details siehe die Diskussion unten). Dies bedeutet, dass diese Art der Konvertierung, obwohl streng möglich, in der Regel nicht sinnvoll ist.

+3

Es ist erwähnenswert, dass, während dies "funktioniert", es ist wahrscheinlich eine strenge Aliasing-Verletzung. – Frank

+0

Ich stimme zu, dass reinterpret_cast das Problem löst. Ich frage mich jedoch, warum int * und E * anders sind als int und E nicht. Wie auch immer, wird als gelöst markieren. – Trilarion

+0

@Trilarion es gibt einige Fälle, in denen es legal ist, 'static_cast' zu verwenden, sie werden explizit aufgelistet. Formal gehört Ihr Fall zu keinem dieser Fälle, also ... müssen Sie statt dessen "reinterpret_cast" verwenden. –

0

Der Standard-Basistyp einer Enumeration ist int und kann explizit im OP angegeben werden. Logischerweise ist der unter E* e gespeicherte Wert, wobei E eine Aufzählung mit Basistyp int ist, ein int. Es kann nicht statisch festgelegt werden.

Es gibt keine Garantie in C++, dass ein enum von Basistyp (beispielsweise) ist das Layout kompatibel mit short aber selbst wenn die Sprache diesen Punkt verschärft könnte es Probleme vom Typ Kompatibilität sein/

Ein Problem ist, dass E* zu int*pi würde die Typ-Sicherheit verletzen, da pi verwendet werden könnte, um Werte außerhalb der Aufzählung ruhig zu setzen. In ähnlicher Weise kann int* bis E* die Typsicherheit verletzen, wenn der ganzzahlige Wert nicht in der Aufzählung enthalten ist.

Hinweis jedoch macht den Standard eine deutlich zur Kenntnis, dass es nichts gibt eine ENUM auszuschließen einen Wert außerhalb der definierten Menge von Werten unter:

Dieser Satz von Werten verwendet wird, Förderung und Konvertierungssemantik für die Auszählung zu definieren Art. Es schließt nicht aus, dass ein Ausdruck des Aufzählungstyps einen Wert aufweist, der außerhalb dieses Bereichs liegt.

Siehe hier: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf (siehe 95 unten p beachten 156.)

Der einzige Fall, könnte (wenn das Layout-Kompatibilität sichergestellt wurden) ist E*-const int* gültig sein, da alle Werte von E*int sind s und der Wert kann durch den Zeiger int * ohne weitere Verletzung des Typsystems nicht (richtig) geändert werden.

Aber ich denke, die Sprachdefinition ist nicht so subtil.

+0

"Logisch der Wert, der bei E * e gespeichert wird, wobei E eine Enumeration mit Basistyp int ist, ist ein int." Während (ich denke) ich weiß, was du meinst, könnte es irreführend sein. Es gibt keine formale Garantie, dass die Darstellung eines Enums der seines Basistyps entspricht (oder nicht, dass ich mir dessen nicht bewusst bin, tbh). Außerdem: "Pi könnte verwendet werden, um Werte außerhalb der Aufzählung ruhig zu setzen" - würde dies auch über ein "int *" geschehen, wäre dies ebenfalls eine Verletzung der strengen Aliasing-Regel. – davmac

+0

Es gibt keine Anweisung, dass Aufzählungen "Layout-kompatibel" mit ihrem "Basistyp" oder "zugrunde liegenden Typ" (AFAIK) sind. Ich verfeinere die Aussage. Ich schlage vor, dass der Standard hier unnötig einschränkend ist. Insbesondere unter Berücksichtigung der Anmerkung 'Diese Gruppe von Werten wird verwendet, um die Semantik der Werbung und der Konversion für den Aufzählungstyp zu definieren. Es schließt nicht aus, dass ein Ausdruck des Aufzählungstyps einen Wert hat, der außerhalb dieses Bereichs liegt. ". Von dem, was ich sehen kann, könnte eine Implementierung 'int' zum Beispiel für eine Enum mit dem Basistyp' short' verwenden. – Persixty