2015-09-18 20 views
18

Das Attribut [[noreturn]] kann auf Funktionen angewendet werden, die nicht zurückgegeben werden sollen. Zum Beispiel:Überschreiben einer virtuellen Funktion [[noremore]]

[[noreturn]] void will_throw() { throw std::runtime_error("bad, bad, bad ...."); } 

Aber ich folgende Situation erlebt habe (nein, ich habe dies nicht Design):

class B { 
public: 
    virtual void f() { throw std::runtime_error(""); } 
}; 

class D : public B { 
    void f() override { std::cout << "Hi" << std::endl; } 
}; 

würde ich wirklich das Attribut [[noreturn]] auf der B::f() Erklärung platzieren möchte. Aber ich bin unklar, was mit der Überschreibung in der abgeleiteten Klasse passiert. Erfolgreiche Rückkehr von einer [[noreturn]] Funktion führt zu undefiniertem Verhalten, und das will ich sicher nicht, wenn eine Überschreibung auch das Attribut erbt.

Die Frage: von [[noreturn] virtual void B::f() überschreiben, tun erben ich das [[noreturn]] Attribut?

Ich habe den C++ 14 Standard durchgesehen, und ich habe Probleme festzustellen, ob Attribute vererbt werden.

+1

Ich weiß nicht, nur raten, aber an dem Punkt, wenn Sie B-> f() aufrufen, wird der Compiler höchstwahrscheinlich keine Ahnung haben, was der dynamische Typ von B ist, also denke ich, dass der Compiler mit noreturn optimieren wird. Ich weiß nicht, warum dieser Entwurf, wenn es, weil B keine Implementierung dafür hat und dieser Wurf ist ein Zeichen von "nenne das nicht, nur bei abgeleiteten Klassen" dann unterschreiben Sie es mit Noreturn macht keinen Sinn mir. (Nun, das Design selbst macht auch keinen Sinn.: D).Wenn Sie den statischen Typ von D verwenden, erwarte ich, dass der Compiler nicht zu noreturn optimiert wird. Nochmal: Ich rate es einfach. – Melkon

+0

's/nicht zur Rückgabe eines Wertes/nicht zur Rückkehr gedacht /' –

+0

@LightnessRacesinOrbit Thanks. Bearbeitetes orig. Post. – KyleKnoepfel

Antwort

1

In der Praxis weder g++, clang noch MSVC betrachten die [[noreturn]] Attribut als geerbt

#include <iostream> 

struct B { 
public: 
    [[noreturn]] virtual void f() { std::cout << "B\n"; throw 0; } 
}; 

struct D : public B { 
    void f() override { std::cout << "D\n"; } 
}; 

int main() 
{ 
    try { B{}.f(); } catch(...) {} 
    D{}.f(); 

    B* d = new D{}; 
    d->f(); 
} 

die "B" ausdruckt, "D" und "D" für alle drei Compiler.

+0

Seit der Rückkehr von einer "Noreturn" -Funktion ist UB, was ist das erwartete Verhalten? – dyp

+1

@dyp, das von 'BB {l.f()' zurückkehrt, gibt eine Warnung auf clang, also hatte ich eine Warnung für 'd.f()' erwartet, wenn noreturn transitiv gewesen wäre (evern wenn UB ndr ist) – TemplateRex

8

Ich habe durch den Standard grepped, und es gibt keine Hinweise darauf, dass entweder [[noreturn]] speziell oder Attribute allgemeiner durch übergeordnete Funktionen "geerbt" werden.

Es ist schwer, ein negativen zu beweisen, und der Standard ist dies eigentlich nicht erklären, so oder so, aber da A::f() und B::f() sind noch verschiedene Funktionen und das einzige Verhalten in Bezug auf den Funktionen definiert beschrieben wird, denke ich, du bist sicher markieren A::f() als [[noreturn]].

Davon abgesehen, kann ich mir nicht vorstellen, welche nützliche Optimierung der Compiler im Anschluss an den dynamischen Versand durchführen könnte.

+2

** + 1 **: Attribute werden nicht vererbt, da sie ([Standardwortlaut] * "gilt" *) für die Entität gelten, in der sie geschrieben wurden. Im OP-Beispiel gilt '[noreturn]]' für 'void B :: f()' - 'void D :: f()' ist eine völlig andere Entität. –

3

Überlegen Sie, was Sie sagen, eigentlich:

class B 
{ 
public: 
    [[noreturn]] virtual void f() { throw std::runtime_error(""); } 
}; 

Gewiß, die menschlichen Leser und möglicherweise der Compiler würde interpretieren diese als ‚Vertrag‘, dh
f() wont Rückkehr, das verspreche ich“

Das sollte dann auch für Überschreibungen von f() gelten, sonst brechen Sie den Vertrag.

Der Standard ist möglicherweise zu niedrig angegeben, aber selbst wenn es funktioniert, würde ich empfehlen, basierend auf Lesbarkeit.

+0

Es ist für mich nicht offensichtlich, dass "[[noreturn]" einen Vertrag mehr spezifiziert als der Zugriffsspezifizierer der Basisklasse einen Vertrag angibt. Der öffentliche/geschützte/private Zugriff einer Funktionsüberschreibung in einer abgeleiteten Klasse ist nämlich unabhängig von dem in der Basisklasse. Der entscheidende Punkt für mich ist jedoch Ihre letzte Aussage. Da der Standard es nicht spezifiziert, fällt es unter unspezifiziertes Verhalten, und ich sollte mich daher nicht darauf verlassen. – KyleKnoepfel

+1

@KyleKnoepfel Der Vertrag ist zwischen Caller und Callee, nicht zwischen Base und Derived. Der Zugangsbezeichner kann als Teil dieses Vertrags angesehen werden. 'public: void f()' ist vielversprechend, dass 'b-> f()' immer verfügbar sein wird, und diese Verheißung gilt auch dann, wenn 'f' von einer privaten Funktion in 'D' überschrieben wird. Ein besseres Argument wäre, dass "[[noreturn]]" nicht mehr Teil des Vertrags ist, als das "= 0" einer rein virtuellen Funktion ist. – Oktalist

Verwandte Themen