2016-01-01 12 views
16

Bedeutet constexpr Spezifizierer implizieren noexcept Spezifizierer für eine Funktion? Answer zu the similar question sagt "ja" bezüglich inline specifier, aber Eric Niebler's article lässt mich über mögliche Antwort auf die aktuelle Frage. Meiner Meinung nach kann die Antwort vom Kontext einer Verwendung einer constexpr-Funktion abhängen: ist es der konstante Ausdrucks- oder Laufzeitkontext, d. H. Sind alle Parameter der Funktion zur Kompilierungszeit bekannt oder nicht.Bedeutet conexpr keine Ausnahme?

Ich erwartete, dass die Antwort "Ja" ist, aber simple check zeigt, dass es nicht der Fall ist.

constexpr 
bool f(int) noexcept 
{ 
    return true; 
} 

constexpr 
bool g(int) 
{ 
    return true; 
} 

static_assert(noexcept(f(1))); 
static_assert(noexcept(g(2))); // comment this line to check runtime behaviour 

#include <cassert> 
#include <cstdlib> 

int 
main(int argc, char * []) 
{ 
    assert(noexcept(f(argc))); 
    assert(noexcept(g(argc))); 
    return EXIT_SUCCESS; 
} 
+0

@ cad sowieso t Die Frage ist sehr allgemein, denke nicht, dass es ein gutes spezifisches Beispiel gibt. – Orient

+0

Gegenbeispiel: 'constexpr void * foo (int n) {return n == 0? nullptr: operator neu (n); } '. [Demo] (https://ideone.com/zlTsDI). –

+0

Ich habe dies einmal missbraucht, siehe http://stackoverflow.com/a/13305072/34509 –

Antwort

13

Nein kann dies nicht der Fall sein, denn nicht jeder inovocation von constexpr Funktion in der Lage sein zu bewerten hat als Teilausdruck eines Kernkonstantenausdrucks. Wir benötigen nur einen Argumentwert, der dies ermöglicht. Eine conexpr-Funktion kann also eine throw-Anweisung enthalten, solange wir einen Argumentwert haben, der diesen Zweig nicht aufruft.

Dies wird in dem Entwurf ++ 14 Standard-Teil C abgedeckt 7.1.5 Der constexpr Spezifizierer [dcl.constexpr], die uns sagt, was in einer constexpr Funktion erlaubt ist:

Die Definition einer constexpr Funktion erfüllen soll die folgenden Randbedingungen:

  • es soll nicht virtuell sein (10.3);

  • Der Rückgabetyp muss ein Literaltyp sein;

  • jeder seiner Parametertypen muss ein Literaltyp sein;

  • seine Funktion Körper wird sein = löschen = default oder eine Verbindung-Anweisung, die nicht

    • eine asm-Definition,

    • eine goto-Anweisung,

    • enthält
    • ein try-block oder

    • eine definition einer variablen vom nicht-literalen typ oder von statisch oder thread st orage duration oder für welche keine Initialisierung durchgeführt wird.

die, wie wir nicht throw und in der Tat wenig verbietet sehr sehen verbietet, da der Relaxing constraints on constexpr functions Vorschlag Teil von C++ 14 wurde.

Im Folgenden finden Sie in der Regel, dass eine constexpr Funktion sagt wohlgeformt ist, wenn mindestens ein Argument Wert existiert, so dass er als Unterausdruck eines Kern konstanten Ausdruck ausgewertet werden kann:

Für eine nicht Vorlage, nicht standardmäßige consExpr-Funktion oder Nicht-Vorlage, nicht-voreingestellt, nicht ererbend consExpr-Konstruktor, Wenn keine Argumentwerte vorhanden sind, kann ein Aufruf der Funktion oder des Konstruktors ein ausgewerteter Unterausdruck eines konstanten Kernausdrucks sein (5.19), das Programm ist schlecht gebildet; nein Diagnose erforderlich.

und unterhalb dieses Absatzes wir das folgende Beispiel haben, die für diesen Fall ein perfektes Beispiel zeigt:

constexpr int f(bool b) 
    { return b ? throw 0 : 0; } // OK 
constexpr int f() { return f(true); } // ill-formed, no diagnostic required 

So würden wir die Ausgabe für das folgende Beispiel erwarten:

#include <iostream> 

constexpr int f(bool b) { return b ? throw 0 : 0; } 

int main() { 
    std::cout << noexcept(f(1)) << "\n" 
       << noexcept(f(0)) << "\n" ; 
} 

sein (see it live with gcc):

0 
1 

Visual Studio über webcompiler erzeugt auch das gleiche Ergebnis. Wie bereits erwähnt, hat clang einen Fehler, wie in dem Fehlerbericht noexcept should check whether the expression is a constant expression dokumentiert.

Defect Bericht 1129

Defect report 1129: Default nothrow for constexpr functions fragt die gleiche Frage:

A constexpr Funktion ist nicht erlaubt, über eine Ausnahme zurückzukehren.Dies sollte erkannt werden, und eine Funktion, die als consExpr ohne eine explizite Ausnahmebestimmung deklariert wird, sollte so behandelt werden, als wäre sie nicht als exception (true) deklariert und nicht als die übliche noexcept (false). Für eine Funktionsvorlage, die mit consExpr ohne explizite Ausnahmespezifikation deklariert wurde, sollte sie nur dann als exception (true) betrachtet werden, wenn das Schlüsselwort constexpr bei einer bestimmten Instantiierung eingehalten wird.

und die Antwort war:

Die Prämisse ist nicht korrekt: eine Ausnahme nur verboten, wenn eine constexpr Funktion in einem Kontext aufgerufen wird, die einen konstanten Ausdruck erfordert. Als gewöhnliche Funktion verwendet, kann es werfen.

und modifizierte 5.3.7 [expr.unary.noexcept] Absatz 3 Punkt 1 (Zusätzlich stellte mit Schwerpunkt):

eine potenziell ausgewertet call80 auf eine Funktion, Member-Funktion, Funktion Zeiger oder Pointer-Elementfunktion, die keine nicht-Wurf exception-Spezifikation hat (15.4 [except.spec]), , wenn der Anruf ein konstanter Ausdruck ist (5,20 [expr.const]),

5

It is said von noexcept, die:

Das Ergebnis ist falsch, wenn der Ausdruck enthält [...] rufen Sie auf jede Art von Funktion, die nicht Nicht-Wurfausnahmespezifikation hat, es sei denn, es ist ein konstanter Ausdruck.

Auch über constexpr, it is true, die:

der noexcept Operator immer true zurück, für einen konstanten Ausdruck

Unter keinen Umständen ist es zu implizieren scheint, dass die constexpr Spezifizierer ein zwingt noexcept Spezifizierer für den umgebenen Ausdruck, wie jemand in den Kommentaren mit einem Gegenbeispiel gezeigt hat und Sie auch verifiziert haben.

Wie auch immer, aus der Dokumentation gibt es folgende interessante Notiz über die Beziehung zwischen noexcept und constexpr:

Da der noexcept Operator gibt immer wahr für einen konstanten Ausdruck, es kann verwendet werden, wenn ein zu prüfen, insbesondere Aufruf einer constexpr Funktion nimmt den konstanten Ausdruck Zweig

EDIT: Beispiel mit GCC

Danke an @hvd für seinen interessanten Kommentar/Beispiel mit GCC über mein letztes Zitat.

constexpr int f(int i) { 
    return i == 0 ? i : f(i - 1); 
} 

int main() { 
    noexcept(f(512)); 
    return noexcept(f(0)) == noexcept(f(0)); 
} 

Der obige Code gibt 0, mit einer Warnung, dass die Aussage noexcept(f(512)) keine Wirkung.
Wenn Sie diese Anweisung, die vermutlich keine Wirkung hat, auskommentieren, ändert sich der Rückgabewert in 1.

EDIT: bekannte Fehler auf Klirren

Nochmals vielen Dank auch für this Link @hvd, ist, dass etwa ein bekannter Fehler in Klirren den Code in Bezug auf in der Frage erwähnt.

Zitat aus dem Bug-Report:

Quoth das Buch von C++, [expr.unary.noexcept] p3:

"Das Ergebnis des noexcept-Operators ist falsch, wenn der Ausdruck in einem potenziell ausgewerteten Kontext einen potenziell ausgewerteten Aufruf an eine Funktion, Elementfunktion, Funktionszeiger oder Mitgliedsfunktionszeiger enthält habe eine nicht-werfende Ausnahme-Spezifikation (15.4), , außer der Aufruf ist ein konstanter Ausdruck (5.19) ".

Wir implementieren diesen letzten Satz nicht.

+1

Vielleicht ein interessantes Beispiel mit GCC von Ihrem letzten Zitat: 'constexpr int f (int i) {Rückkehr i == 0? i: f (i - 1); } int main() {noexcept (f (512)); Rückgabe noexcept (f (0)) == noexcept (f (0)); } '. Dies gibt "0" zurück, mit einer Warnung, dass die Anweisung "noexcept (f (512));" keine Auswirkung hat. Wenn Sie diese Anweisung, die vermutlich keine Wirkung hat, auskommentieren, ändert sich der Rückgabewert in "1". – hvd

+0

@hvd Ich denke, ein Beispiel kann in der verknüpften Dokumentation auch gefunden werden, trotzdem danke, interessanter Kommentar. – skypjack

+0

Wie für den Code in der Frage, [das ist bereits als ein Bug in Clang bekannt] (https://llvm.org/bugs/show_bug.cgi?id=15481). Ich würde es normalerweise als eine separate Antwort posten, aber in diesem Fall denke ich, es ist klein genug, dass Sie es in Ihre Antwort aufnehmen können, wenn Sie möchten. – hvd

2

Nein, im Allgemeinen nicht.

Eine constexpr-Funktion kann in einem non-constepr-Kontext aufgerufen werden, in dem eine Ausnahme ausgelöst werden kann (außer natürlich, wenn Sie sie manuell als noexcept(true) angeben).

Als Teil eines konstanten Ausdrucks (wie in Ihrem Beispiel) sollte er sich jedoch wie noexcept(true) verhalten (wenn die Auswertung des Ausdrucks zu einer Ausnahme führen würde, kann dies natürlich nicht passieren) zu einem Aufruf von std::terminate führen, da das Programm noch nicht läuft, sondern stattdessen zu einem Kompilierzeitfehler führt).

Wie ich erwartet hätte, löst das Beispiel nicht die statische Assertion mit MSVC und g ++ aus. Ich bin mir nicht sicher, ob das ein Bug im Klirren ist oder ich etwas Missverständliches im Standard verstehe.

+0

In welcher Weise bedeutet das dann "noexcept (true)", wenn Sie bereits sagen, dass die Wirkung von "noexcept (true)" nicht zutrifft? – hvd

+0

@hvd: Wie skypjack zitiert: Anwendung des noexcept-Operators auf eine constexpr-Funktion in einem consxpr-Kontext (wie in der Frage ergibt true, so als ob diese Funktion deklariert wurde (noexcept (true)). – MikeMB

+0

true) 'hat zwei Effekte: Erstens macht es Ausnahmen das Programm zu beenden, zweitens bewirkt es, dass der Funktionsaufruf das Ergebnis des 'noexcept'-Operators nicht beeinflusst, er hat diesen ersten Effekt nicht, er verhält sich ähnlich zu diesem zweiten Effekt, aber nicht genau auf die gleiche Weise, und in jedem Fall wird dieser zweite Effekt nicht erreicht, indem implizit die Funktion noexcept (true) gemacht wird, sondern durch eine zusätzliche Ausnahme in den Regeln des noexcept-Operators. wie in @ skypjacks Antwort gezeigt: – hvd

2

Sie dürfen eine Exception in einer conexpr-Funktion auslösen. Es ist so konzipiert, dass der Implementierer dem Compiler einen Fehler anzeigen kann. Bedenken Sie, dass Sie die folgende Funktion haben:

constexpr int fast_sqrt(int x) { 
    return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x); 
} 

In diesem Fall, wenn x negativ ist, dass wir Kompilierung sofort und zeigen das Problem an den Benutzer über Compiler-Fehler stoppen müssen. Dies folgt der Idee, dass Compiler-Fehler besser sind als Laufzeitfehler (Fail-Fast).

C++ Standard sagt dies in (5,20):

Ein bedingter Ausdruck e ein Kern konstanter Ausdruck, es sei denn die Auswertung von e ist nach den Regeln der abstrakten Maschine (1,9), würde man bewerten der folgenden Begriffe:

- Einwurf Ausdruck (5,17)

+0

Ihr Standard unterstützt Ihre Antwort nicht vollständig. Sie müssen auch '7.1.5' wie in [meine Antwort] (http://stackoverflow.com/a/34558600/1708801) angeben, um zu bestätigen, dass es sich um einen gültigen Code handelt . –

+0

@Shafik, ich suchte nach dem Teil, der die Kriterien für eine constexpr-Funktion erfüllt. Danke für die Aufnahme! – Andrew