Wann wird eine constexpr-Funktion zur Kompilierzeit ausgewertet?

Lesezeit: 5 Minuten

Da es möglich ist, dass eine als constexpr deklarierte Funktion zur Laufzeit aufgerufen werden kann, nach welchen Kriterien entscheidet der Compiler, ob er sie zur Kompilierzeit oder zur Laufzeit berechnet?

template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
    return (expo != 0 )? base * POW(base, expo -1) : 1;
}

int main(int argc, char** argv)
{
    int i = 0;
    std::cin >> i;

    std::cout << POW(i, 2) << std::endl;
    return 0;
}

In diesem Fall ist i zur Kompilierzeit unbekannt, was wahrscheinlich der Grund ist, warum der Compiler POW() als reguläre Funktion behandelt, die zur Laufzeit aufgerufen wird. Diese Dynamik hat jedoch, so bequem sie erscheinen mag, einige unpraktische Implikationen. Könnte es zum Beispiel einen Fall geben, in dem ich möchte, dass der Compiler während der Kompilierzeit eine constexpr-Funktion berechnet, in dem der Compiler beschließt, sie stattdessen als normale Funktion zu behandeln, wenn es auch während der Kompilierzeit funktioniert hätte? Gibt es bekannte typische Fallstricke?

  • AFAIK, wenn alle Argumente konstante Ausdrücke sind.

    – Chris

    9. Januar 2013 um 23:19 Uhr

  • @chris Und was ist, wenn ich schreibe POW((unsigned __int64)2, 63). Würde das immer noch als konstanter Ausdruck gelten?

    – byzantinisch

    9. Januar 2013 um 23:21 Uhr


  • @chris: Eigentlich ist es komplexer als das, denke ich. ich Überlegen constexpr muss nur ausgewertet werden, wenn sein Ergebnis als Vorlagenparameter, Array-Grenze oder andere ganzzahlige Konstante verwendet wird. Jede andere Zeit ist eine Optimierung. In der Tat könnte dies der Fall sein, selbst wenn Argumente für konstante Ausdrücke angegeben werden erforderlich zur Laufzeit auszuführen. constexpr int func(int p) { return !p ? 1 : throw std::exception("HI");} Muss zur Laufzeit ausgewertet werden, wenn eine Eingabe ungleich Null gegeben wird.

    – Muhende Ente

    9. Januar 2013 um 23:21 Uhr


  • Initialisierer, die konstante Ausdrücke sind, bilden einen Teil der statischen Initialisierungsphase, z constexpr int a = POW(5, 4);. Das wird im Wesentlichen zur Kompilierzeit berechnet. Kannst du aber natürlich trotzdem verwenden POW an anderen Orten.

    – Kerrek SB

    9. Januar 2013 um 23:23 Uhr


  • @MooingDuck: Sofern das Ergebnis der Funktion nicht in Ihrem oben genannten konstanten Ausdruck “requirerers” verwendet wird, wird aufgrund der Ausnahme ein Kompilierzeitfehler ausgegeben.

    – GManNickG

    9. Januar 2013 um 23:34 Uhr

Wann wird eine constexpr Funktion zur Kompilierzeit ausgewertet
K-ballo

constexpr Funktionen Wille zur Kompilierzeit ausgewertet werden, wenn alle seine Argumente konstante Ausdrücke sind und das Ergebnis auch in einem konstanten Ausdruck verwendet wird. Ein konstanter Ausdruck könnte ein Literal sein (wie 42), ein Nicht-Typ-Template-Argument (wie N in template<class T, size_t N> class array;), ein enum Elementdeklaration (wie Blue in enum Color { Red, Blue, Green };eine weitere Variable deklariert constexprund so weiter.

Sie könnte ausgewertet werden, wenn alle seine Argumente konstante Ausdrücke sind und das Ergebnis ist nicht in einem konstanten Ausdruck verwendet, aber das ist Sache der Implementierung.

  • @mcmcc versuchen Sie, das Ergebnis als Nicht-Typ-Vorlagenparameter zu verwenden. dies sollte integral constexpr abdecken. für float brauchst du was anderes.

    – TemplateRex

    11. Januar 2013 um 19:39 Uhr


  • Wo im Standard gibt es eine Garantie dafür constexpr Funktionen werden zur Kompilierzeit ausgewertet, wenn alle Argumente konstante Ausdrücke sind? Ich glaube, das einzige, was der Standard sagt, ist das constexpr Funktionen können in Kontexten verwendet werden, die zur Kompilierzeit ausgewertet werden müssen, z. B. Template-Argumente. Alles andere entscheidet der Compiler. Zumindest habe ich das bisher geglaubt.

    – Jogojapan

    16. Januar 2013 um 4:36 Uhr

  • @jogojapan: Laut sowohl Bjarne als auch Herb zurück im ISO-C++ Post, “Die richtige Antwort – wie von Herb angegeben – lautet, dass eine constexpr-Funktion laut Standard zur Compiler- oder Laufzeit ausgewertet werden kann, es sei denn, sie wird als konstanter Ausdruck verwendet. In diesem Fall muss sie zur Kompilierzeit ausgewertet werden.”. Das würde das bedeuten a in deinem beispiel Muss zur Kompilierzeit ausgewertet werden und macht die Notiz nicht geradezu falsch. Ich bin mir nicht sicher, ob dies zu diesem Zeitpunkt tatsächlich durch den Standard garantiert wird …

    – K-ballo

    16. Januar 2013 um 6:37 Uhr


  • @mcmcc: Für dein “etwas anderes”, warum schreibst du nicht a constexpr Funktion, die einen Gleitkommawert (oder tatsächlich einen beliebigen Typ) akzeptiert und eine Ganzzahl zurückgibt, verwenden Sie diese dann in einem Vorlagenparameter, der kein Typ ist. constexpr size_t check_nonintegral_constexpr(float v) { return sizeof v; } std::array<check_nonintegral_constexpr(pow(2.0, 4))> assertion;

    – Ben Voigt

    1. April 2013 um 21:59 Uhr

  • “Und das Ergebnis wird auch in einem konstanten Ausdruck verwendet”. Damit hatte ich zu kämpfen. Danke.

    – Karliwson

    7. Dezember 2016 um 3:49 Uhr

1646890810 510 Wann wird eine constexpr Funktion zur Kompilierzeit ausgewertet
Kneipe

Die Funktion muss zur Kompilierzeit ausgewertet werden, wenn ein konstanter Ausdruck benötigt wird.

Die einfachste Methode, dies zu gewährleisten, ist die Verwendung von a constexpr Wert bzw std::integral_constant:

constexpr auto result = POW(i, 2); // this should not compile since i is not a constant expression
std::cout << result << std::endl;

oder:

std::cout << std::integral_constant<int, POW(i, 2)>::value << std::endl;

oder

#define POW_C(base, power) (std::integral_constant<decltype(POW((base), (power)), POW((base), (power))>::value)

std::cout << POW_C(63, 2) << std::endl;

oder

template<int base, int power>
struct POW_C {
  static constexpr int value = POW(base, power);
};

std::cout << POW_C<2, 63>::value << std::endl;

  • Bedeutet das das std::cout << POW(2, 63) << std::endl könnte während der Kompilierzeit nicht ausgewertet werden, da cout keinen konstanten Ausdruckswert benötigt?

    – byzantinisch

    9. Januar 2013 um 23:30 Uhr

  • @cyberpunk_ Ja. Tatsächlich sollten Sie davon ausgehen, dass es zur Laufzeit aufgerufen wird, da aktuelle Compiler dies zu tun scheinen.

    – Kneipe

    9. Januar 2013 um 23:32 Uhr

  • @cyberpunk_ Die Argumente für constexpr-Funktionen sind keine konstanten Ausdrücke und das würde nicht helfen.

    – Kneipe

    9. Januar 2013 um 23:48 Uhr

  • @cyberpunk_: Dafür solltest du Referenzen verwenden. (Nicht, dass das Kopieren eines Kompilierzeitwerts etwas kosten würde …)

    – GManNickG

    10. Januar 2013 um 1:05 Uhr

  • @balki Hier ist, was ich mir ausgedacht habe, aber ich bin mir nicht sicher, ob es die bestmögliche Lösung ist. Ich bin mir auch nicht sicher, ob die Rückgabe einer Rvalue-Referenz tatsächlich auf diese Weise funktioniert. #define FORCE_CT_EVAL(func) [](){constexpr auto ___expr = func; return std::move(___expr);}()

    – byzantinisch

    12. Januar 2013 um 13:46 Uhr


986440cookie-checkWann wird eine constexpr-Funktion zur Kompilierzeit ausgewertet?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy