Dynamische Besetzung im Destruktor

8

Ist dieser Code legal?

class Base1 {
};

class Base2 {
public:
    virtual ~Base2() {
        if (!dynamic_cast<Base1*>(this))
            std::cout << "aaaa" << std::endl;
    }
    Base2() {
    }
};

class MyClass: public Base1, public Base2 {
public:
    MyClass() {
    }
    virtual ~MyClass() {
        std::cout << "bbb" << std::endl;
    }
};

int main() {
    MyClass s;
    return 0;
}

Ich sehe beide Drucke, aber ich sollte nur einen sehen. Ich denke, die dynamische Besetzung ist falsch. Ist es möglich, einen solchen Scheck durchzuführen?

greywolf82
quelle
Können Sie klarstellen, was Sie überprüfen möchten? Möchte die Base2 wissen, ob es sich um eine Basis einer abgeleiteten Klasse handelt, die auch eine Base1 hat?
jtbandes
Ja, ich möchte in Base2 einchecken, ob "this" auch ein Kind von base1 ist
greywolf82
"aber ich sollte nur einen sehen" warum? und warum hast du zweifel an der legalität?
idclev 463035818
1
@ greywolf82 oh sorry ich habe das verpasst!
idclev 463035818
2
Verhält dynamic_castsich in Konstruktoren und Destruktoren nicht anders?
François Andrieux

Antworten:

7

Vielleicht habe ich die Lösung selbst gefunden, die Antwort ist nein, es ist nicht möglich:

Aus Punkt 6 der Dokumentation zu cppreference.com :

Wenn dynamic_cast in einem Konstruktor oder Destruktor (direkt oder indirekt) verwendet wird und der Ausdruck sich auf das Objekt bezieht, das derzeit erstellt / zerstört wird, wird das Objekt als das am meisten abgeleitete Objekt betrachtet. Wenn new-type kein Zeiger oder Verweis auf die eigene Klasse des Konstruktors / Destruktors oder eine seiner Basen ist, ist das Verhalten undefiniert.

Siehe auch [class.cdtor] / 6 des Standards.

Da ich im Base2-Destruktor auf Base1 umwandle, ist dieses Verhalten undefiniert.

greywolf82
quelle
1
[class.cdtor]/6, als Referenz. Sorry, nicht 5. War 5 in C ++ 17 (Entwurf N4659), sieht aus wie es /6jetzt ist.
ChrisMM
@ChrisMM In diesem Absatz scheint das undefinierte Verhalten, auf das in dieser Antwort verwiesen wird, nicht zu erwähnen. Eigentlich konnte ich diese Einschränkung nirgendwo im Standard finden.
Walnuss
Ich denke, cppreference hat fälschlicherweise " new-type " geschrieben, wenn es " expression " hätte sein sollen. Die verknüpfte Standardpassage besagt, dass der Operand einen (statischen) Typ der Destruktorklasse oder eine Basis davon haben muss, wenn er sich auf das zu zerstörende Objekt bezieht. Der Code der Frage thisist vom Typ des Destruktors, daher sehe ich nicht, dass UB angewendet wird. Der gleiche Standardabsatz besagt jedoch, dass der am meisten abgeleitete Typ des zu zerstörenden Objekts als die Klasse des Destruktors betrachtet wird, so dass das in der anderen Antwort erläuterte Verhalten beobachtet wird.
Walnuss
@walnut, habe gerade die relavante Passage aus dem Standard gepostet, nicht meine Antwort. Ich bin damit einverstanden, dass es nicht UB ist.
ChrisMM
3

Ich stimme der Antwort von @ j6t zu, aber hier ist eine erweiterte Argumentation mit Standardreferenzen.

Das spezielle Verhalten von dynamic_castObjekten, die sich im Bau und in der Zerstörung befinden, wird in [class.cdtor] / 5 des C ++ 17-Standards (endgültiger Entwurf) und in früheren Standardversionen beschrieben.

Insbesondere heißt es:

Wenn a dynamic_­cast[...] in einem Destruktor verwendet wird, [...] wenn sich der Operand von dynamic_­castauf das im Aufbau oder in der Zerstörung befindliche Objekt bezieht, wird dieses Objekt als das am meisten abgeleitete Objekt mit dem Typ [ ...] Zerstörerklasse. Wenn der Operand von dynamic_­castauf das Objekt unter [...] Zerstörung verweist und der statische Typ des Operanden kein Zeiger oder Objekt auf die eigene Klasse des [...] Destruktors oder eine seiner Basen ist, führt der dynamic_cast zu undefiniertes Verhalten.

Das undefinierte Verhalten gilt hier nicht, da der Operand der Ausdruck ist this, der trivial den Typ eines Zeigers auf die eigene Klasse des Destruktors hat, da er im Destruktor selbst erscheint.

Der erste Satz besagt jedoch, dass sich der dynamic_castWille so verhält, als wäre er *thisein am meisten abgeleitetes Objekt vom Typ, Base2und daher kann die Umwandlung in Base1niemals erfolgreich sein, da er Base2nicht von abgeleitet Base1ist und dynamic_cast<Base1*>(this)immer einen Nullzeiger zurückgibt, was zu dem angezeigten Verhalten führt.


cppreference.com gibt an, dass das undefinierte Verhalten auftritt , wenn der Zieltyp der Umwandlung nicht der Typ der Destruktorklasse oder eine ihrer Basen ist, anstatt dies auf den Operandentyp anzuwenden. Ich denke das ist nur ein Fehler. Wahrscheinlich sollte die Erwähnung von " neuem Typ " in Punkt 6 " Ausdruck " sagen , was dazu führen würde, dass es meiner obigen Interpretation entspricht.

Nussbaum
quelle
2

Das dynamic_castist in dieser Situation gut definiert. Es ist richtig, dass Sie beide Ausgabezeilen beobachten.

Es ist falsch anzunehmen, dass im Destruktor von Base2 thiseine abgeleitete Klasse ist. Zu diesem Zeitpunkt wurde der abgeleitete Klassenteil bereits zerstört, sodass er keine abgeleitete Klasse mehr sein kann. Tatsächlich ist zu dem Zeitpunkt, an dem der Destruktor ausgeführt wird Base2, das Objekt, auf das gezeigt this wird, nur ein Base2Objekt. Da Base2nicht in Bezug auf Base1in irgendeiner Weise, die dynamic_castliefert einen Null - Zeiger, und die bedingten entsprechend eingegeben.

Bearbeiten: Der Standard sagt :

Wenn a dynamic_­castin einem Konstruktor [...] oder in einem Destruktor [...] verwendet wird und sich der Operand von dynamic_­castauf das im Aufbau oder in der Zerstörung befindliche Objekt bezieht, wird dieses Objekt als das am meisten abgeleitete Objekt mit dem Typ betrachtet der Konstruktor- oder Destruktorklasse. Wenn sich der Operand von dynamic_­castauf das im Aufbau oder in der Zerstörung befindliche Objekt bezieht und der statische Typ des Operanden kein Zeiger auf oder Objekt der eigenen Klasse des Konstruktors oder Destruktors oder einer seiner Basen ist, dynamic_­castführt dies zu undefiniertem Verhalten.

Der Operand thisbezieht sich auf das zu zerstörende Objekt. Daher wird die Klasse des Destruktors ( Base2) als die am meisten abgeleitete Klasse betrachtet, und dies ist der Grund, warum das Objekt Base1*in keiner Weise mit dem Zieltyp ( ) verknüpft ist . Darüber hinaus ist die statische Typ des Operanden thisist Base2* const, die eindeutig ist ein Zeiger auf die eigene Klasse destructor. Daher gilt die Regel über undefiniertes Verhalten nicht. Zusammenfassend haben wir ein klar definiertes Verhalten.

j6t
quelle
1
Dies widerspricht der anderen Antwort, die besagt (aus dem Standard zitiert), dass der Code ein undefiniertes Verhalten aufweist. Sie brauchen einige gute Argumente, um dies zu beanstanden, indem
Sie
1
@ ehemalsknownas_463035818 Die andere Antwort zitiert aus cppreference, nicht aus dem Standard. Das wäre vielleicht nicht klar gewesen. Der Standardabsatz, den es erwähnt, scheint nicht dasselbe zu sagen wie cppreference.
Walnuss
@walnut Ich dachte, es wäre ein Zitat aus dem Standard, während es bearbeitet wurde, um aus cppref zu zitieren. Wie auch immer, die Antworten sind widersprüchlich, einer hat Quellen, um sie zu
sichern
@walnut Ich habe mich selbst überprüft und [class.cdtor]/6in der anderen Antwort erwähnt, dass cppref dasselbe bedeutet: "Wenn der Operand des dynamic_cast auf das im Aufbau oder in der Zerstörung befindliche Objekt verweist und der statische Typ des Operanden kein Zeiger auf oder Objekt des Konstruktors oder ist Der Dynamic_cast ist die eigene Klasse des Destruktors oder eine seiner Basen und führt zu undefiniertem Verhalten. "
idclev 463035818
1
Ich habe meine Interpretation des Standards hinzugefügt.
30.