C++11 vtbl, inline und final
-
Hi,
bisher habe ich die Definition jeder auch noch so kurzen virtuellen Methode nicht in den Header geschrieben, da Inlining ja nicht möglich ist. In C++11 kann ich jetzt allerdings eine Klasse mit einem
finalversehen um zu verhindern, dass irgendjemand auf die Idee kommt, Erben zu wollen. Wenn man jetzt eine virtuelle Methode einer finalen Klasse aufruft, die im Header definiert ist, weiß der Compiler doch, welche Definition er aufrufen muss. Das Inlining sollte in diesem Fall wieder möglich sein. Ein Beispiel:struct Animal { virtual int weight() const = 0; }; struct Pig : public Animal { int weight() const override { return 100; } }; // Final struct Bird : public Animal final { int weight() const override { return 1; } }; int calcShippingPrice(const Pig& pig, int dperkg) { // Kein inlining möglich return pig.weight() * dperkg; } int calcShippingPrice(const Bird& bird, int dperkg) { // Inlining möglich? return bird.weight() * dperkg; }Und nicht nur final-Klassen sollten inline-taugliche virtuelle Methoden im Header definieren, da vielleicht eine final-Klasse von ihr erbt und die Methode nicht überschreibt. Nur dann ist wieder Inlining möglich:
struct Animal { virtual int weight() const { return 10; } }; // Final - übernimmt Definition von Animal struct Pig final { }; int calcShippingPrice(const Pig& pig, int dperkg) { // Inlining möglich return pig.weight() * dperkg; }Stimmt meine Theorie?
-
Eine kleine Berichtigung:
Im ersten Beispiel ist das final beim Bird verrutscht. Diese Zeile soll so sein:struct Bird final : public Animal {
-
da Inlining ja nicht möglich ist.
Stimmt so nicht. vtable kommt erst ins Spiel, wenn der Typ dynamisch ist, also zur Compilezeit noch nicht feststeht. Statisches Binding geht natürelich trotz virtual weiterhin:
Test t; t.foo();t ist zur Compilezeit bekannt, foo könnte also problemlos geinlined werden, auch wenn es virtual wäre.
-
Aber woher soll denn calcShippingPrice für Pigs wissen, ob es nicht von einer Klasse aufgerufen worden ist, die von Pig geerbt hat? Der Typ ist in dem Fall dynamisch und Inlining nicht möglich.
-
Ich denke prinzipiell stimmt deine Theorie. Allerdings ist da nur die Frage ob es ein Compiler auch macht. Ist, denke ich, nicht so schwer (im Vergleich mit anderem Zeugs). Also kann es durchaus sein, dass es implementiert wird.
Allerdings könnte man inlineing auch für dynamische Typen machen. Zumindest für manche Fälle. Mit Data Flow Analysis könnte man da wahrscheinlich bereits viele herkömmliche Konstrukte dennoch inlinen (weil nicht komplett dynamisch).
-
Der Compiler kann bei dynamischen Typen kein Inlining machen, ohne den ganzen Code zu kennen. Schliesslich kann man in der hintersten Ecke des Codes eine Klasse ableiten, welche die Funktion überschreibt.
drakon schrieb:
prinzipiell ... Allerdings ... denke ich ... kann es durchaus sein ... Allerdings könnte ... Zumindest ... könnte ... wahrscheinlich
Da lehnst du dich aber gerade etwas aus dem Fenster

-
Finaliz0r schrieb:
drakon schrieb:
prinzipiell ... Allerdings ... denke ich ... kann es durchaus sein ... Allerdings könnte ... Zumindest ... könnte ... wahrscheinlich
Da lehnst du dich aber gerade etwas aus dem Fenster

Nein. Es geht darum, dass es möglich ist. Ob ein Compiler das macht ist eine andere Frage. Ich weiss nicht ob es gängige Compiler machen, aber ich weiss, dass es ginge.

Nicht immer, da stimme ich dir zu, aber in einigen Fällen.Animal a = 0; if(...) a = new Pig(1); else a = new Pig(2); a.weight();Wäre z.B ein Fall welchen man ganz gut finden kann.
Ich bin in meinen Aussagen nur eher vorsichtig, wenn ich nicht 100% weiss, dass es geht. Ich behaupte nicht einfach so ins blaue hinaus, wie es einige tun.

-
vptr schrieb:
Aber woher soll denn calcShippingPrice für Pigs wissen, ob es nicht von einer Klasse aufgerufen worden ist, die von Pig geerbt hat? Der Typ ist in dem Fall dynamisch und Inlining nicht möglich.
der gcc inlined mir solche Fälle problemlos. Sogar beo folgender Signatur wird weight() inline, sofern der Compiler irgendwie aus dem umliegenden Code erschließen kann, dass der Typ fix ist.
int calcShippingPrice(const Animal& pig, int dperkg)War echt ärgerlich, als ich eine solche Funktion profilen wollte, die dann aber im optimierten Code gar nicht mehr vorkam...
-
vptr schrieb:
[...]
[...] struct Animal { virtual int weight() const { return 10; } }; struct Bird final : public Animal { int weight() const override { return 1; } }; int calcShippingPrice(const Bird& bird, int dperkg) { // Inlining möglich? return bird.weight() * dperkg; }In einem Programm, was an dieser Stelle kein undefiniertes Verhalten hervorruft, kann .weight in diesem Fall ja nur .Bird::weight sein. Deswegen wäre es völlig legal für einen Compiler, hier auch zu inlinen und die return-Zeile zu "return dperkg;" optimieren. Vor undefiniertem Verhalten muss er sich ja nicht extra schützen.
meine zwei Eurocents,
kk