2015-12-14 11 views
7

Zum Beispiel unter dem Code nicht kompiliert, es sei denn incr()constexpr deklariert ist:Wo in C++ 14 Standard heißt es, dass eine non-consxpr-Funktion nicht in einer Definition einer constexpr-Funktion verwendet werden kann?

int incr(int& n) { 
    return ++n; 
} 

constexpr int foo() { 
    int n = 0; 
    incr(n); 
    return n; 
} 

bei der Suche §7.1.5/3 in C++ 14 haben wir:

Die Definition eines Die consExpr-Funktion muss folgende Bedingungen erfüllen:
(3.1) - es darf nicht virtuell sein (10.3);
(3.2) - sein Rückgabetyp muss ein Literaltyp sein;
(3.3) - jeder seiner Parametertypen muss ein Literaltyp sein;
(3.4) - seine Funktion Körper wird sein = löscht = default oder eine Verbindung-Anweisung, die nicht

(3.4.1) enthält - eine asm-Definition,
(3.4.2) - eine GOTO-Anweisung,
(3.4.3) - ein try-Block oder
(3.4.4) - eine Definition einer variablen von nicht-Literal-Typ oder von statischer oder Fadenspeicherdauer oder bei denen keine Initialisierung ist durchgeführt.

+0

Wahrscheinlich erwähnenswert, die [vorherige Frage] (http://stackoverflow.com/q/34211688/1708801), die eine Menge von dem gleichen Boden bedeckt, obwohl aus einer anderen Perspektive . –

+0

@ShafikYaghmour Ich würde mich sehr über Ihre Kommentare zu dieser neuen Frage freuen. – Ayrosa

+0

Beachten Sie, dass die Dinge, die Sie zitiert haben, sind * notwendig *, aber nicht * ausreichend * Bedingungen, dh wenn diese Bedingungen verletzt werden dann ist der Code schlecht gebildet, aber wenn sie nicht verletzt werden, dann kann der Code richtig sein und wir muss auf andere Teile der Spezifikation schauen. –

Antwort

10

Zwei Absätze später in [dcl.constexpr]/5:

Für eine nicht-Vorlage, nicht vorbelegt constexpr Funktion oder eine nicht-Vorlage, nicht vorbelegt, die keine Vererbungs conexpr-Konstruktor, wenn keine Argumentwerte vorhanden sind, so dass ein Aufruf der Funktion oder des Konstruktors ein ausgewerteter Teilausdruck eines Kernkonstantenausdrucks (5.20) oder für einen Konstruktor ein Konstant Initialisierer für ein Objekt sein kann (3.6.2) das Programm ist schlecht geformt; keine Diagnose erforderlich.

kein Argument gibt, so dass foo() wegen incr() ein Kern konstanter Ausdruck sein könnte, daher wird das Programm schlecht ausgebildet (NDR).

+0

Ich denke nicht, dass §7.1.5/5 nichts mit der Frage zu tun hat. Zum Beispiel, wo im Standard heißt es, dass der Körper einer consExpr-Funktion ein konstanter Ausdruck sein muss? – Ayrosa

+0

@Ayrosa: Genau dort in diesem Zitat. "Ein Aufruf der Funktion oder des Konstruktors könnte ein ausgewerteter Teilausdruck eines Kernkonstantenausdrucks sein." Aber es muss nicht der gesamte Körper sein, sondern nur die bewerteten Teile. –

+0

@BenVoigt Aber es sagt noch nicht, dass der Körper einer constexpr-Funktion ein konstanter Kernausdruck ist. – Ayrosa

4

Es tut es nicht.

Nachstehend ist erlaubt, obwohl es genau das tut, was Sie vermuten, ist verboten:

int incr(int& n) { 
    return ++n; 
} 

constexpr int foo(bool x) { 
    int n = 0; 
    if (x) incr(n); 
    return n; 
} 

Der Code in Ihrer Frage nach der Regel mit Barry nicht erlaubt ist in seiner Antwort zitiert. Aber in meiner Variation gibt es eine Reihe von Parametern (speziell false), mit denen der Aufruf von foo zu einem konstanten Ausdruck der Kompilierungszeit führt.

Beachten Sie, dass eine Diagnose nicht erforderlich ist - ein entsprechender Compiler könnte auch die Kompilierung Ihrer Version ermöglichen.

+1

Es ist erwähnenswert, dass g ++ beide Versionen nicht kompiliert und die gleiche (falsche) Fehlermeldung in beiden Fällen gibt. clang ++ kompiliert diesen und weist den ursprünglichen unter Berufung auf den richtigen Grund zurück: "Die Funktion constexpr erzeugt niemals einen konstanten Ausdruck". –

+1

@ n.m. Dieser Fehler wurde in gcc trunk behoben. – bames53

+0

Es hat mich irgendwann dazu gebracht, die Qualität beider Antworten zu schätzen, deins und bames53 (+1). – Ayrosa

8

Was Sie suchen ist § 5.19:

A bedingungsausdrucke ein Kern konstanter Ausdruck, es sei denn die Bewertung der e ist nach den Regeln der abstrakten Maschine (1.9), wäre eine der folgenden Ausdrücke bewerten:

Dies gilt für die Auswertung eines Ausdrucks, der ein constexpr Funktionsaufruf ist. Das heißt, das Aufrufen einer constexpr-Funktion wird ein "konstanter Kernausdruck" sein, wenn das Auswerten der Funktion, dh das Ausführen des Rumpfs der Funktion gemäß den Regeln der abstrakten C++ - Maschine, keines der in der Liste verbotenen Dinge tut in § 5.19.

Eines der Elemente in der Liste ist:

  • ein Aufruf einer anderen Funktion als [...] eine constexpr Funktion

So dies zu Ihrem Beispiel anwenden : Auswerten des Ausdrucks foo() wertet einen Aufruf für eine Funktion incr() aus, die keine constexpr-Funktion ist, was bedeutet, dass der Ausdruck foo() kein Ausdruck für die Kernkonstante ist.

Weiter, da das oben für alle möglichen Aufrufe Ihrer Funktion foo gilt, tritt die Regel in § 7.1.5/5 in Kraft und bedeutet, dass Ihr Beispielprogramm schlecht ausgebildet ist, keine Diagnose erforderlich, auch wenn Sie nie rufen Sie tatsächlich foo().


Wie Ben Voigt weist eine constexpr Funktion aus können Anrufe an Nicht-consexpr Funktionen enthalten, solange die besondere Auswertung der Funktion wertet nicht tatsächlich eine solche Funktionsaufruf (oder scheint es in einem Kontext, der tut nicht einen konstanten Ausdruck erfordern).

Die Einschränkungen in 5.19 beziehen sich nur darauf, welche Ausdrücke tatsächlich als Teil der Auswertung eines Ausdrucks ausgewertet werden.

Zum Beispiel:

#include <iostream> 

int incr(int &n) { return ++n; } 

enum E {be_constexpr, not_constexpr}; 

constexpr int foo(E e = be_constexpr) { 
    int n = 0; 
    if (e == not_constexpr) { incr(n); } 
    return n; 
} 

int main() { 
    constexpr int a = foo(); // foo() is a constant expression 
    int b = foo(not_constexpr); // may or may not evaluate `foo(non_constexpr)` at runtime. In practice modern C++ compilers will do compile-time evaluation here even though they aren't required to. 
    // constexpr int c = foo(not_constexpr); // Compile error because foo(not_constexpr) is not formally a constant expression, even though modern compilers can evaluate it at compile-time. 

    std::cout << a << ' ' << b << '\n'; 
} 
+0

Es hat mich irgendwann wirklich gekümmert, die Qualität beider Antworten zu schätzen, deine und Ben Voigts (+1). – Ayrosa