zurück zum Artikel

constexpr Funktionen

Rainer Grimm

Nach der Template-Metaprogrammierung und der Type-Traits-Bibliothek geht es heute vor allem um constexpr-Funktionen.

Nach der Template-Metaprogrammierung und der Type-Traits-Bibliothek geht es heute vor allem um constexpr-Funktionen.

constexpr Funktionen

Ich habe in den letzten Jahren bereits ein paar Beiträge über constexpr geschrieben. Hier ist meine Motivation: Erstens werde ich interessante Ähnlichkeiten zwischen constexpr Funktionen und Templaten aufzeigen. Zweitens möchte ich über die Verbesserungen von constexpr in C++20 schreiben. Und schließlich gehe ich auch auf consteval in C++20 ein. Wenn eine Theorie in meinen Beiträgen nicht ausführlich genug ist, werde ich auf frühere Beiträge verweisen. Beginnen wir mit einer kurzen Zusammenfassung, bevor ich auf die neuen Themen eingehe.

constexpr ermöglicht es, zur Compilezeit mit der typischen C++-Syntax zu programmieren. Konstante Ausdrücke mit constexpr können drei Formen haben.

constexpr double pi = 3,14;

constexpr Funktionen in C++14 sind recht komfortabel. Sie können

Die Regeln für constexpr Funktionen oder Memberfunktionen sind simpel. Der Einfachheit wegen, nenne ich beide Funktionen.

constexpr Funktionen müssen alle ihr Abhängigkeit zur Compliezeit auflösen können. Eine constexpr-Funktion zu sein, bedeutet nicht, dass die Funktion zur Compilezeit ausgeführt wird. Es bedeutet, dass die Funktion das Potenzial hat, zur Compilezeit ausgeführt zu werden. Eine constexpr-Funktion kann auch zur Runtime ausgeführt werden. Es ist oft eine Frage des Compilers und der Optimierungsstufe, ob eine constexpr Funktion zur Compilezeit oder zur Runtime ausgeführt wird. Es gibt zwei Kontexte, in denen eine constexpr-Funktion func zur Compilezeit ausgeführt werden muss.

Hier ist ein kleines Beispiel zur Theorie. Das Programm constexpr14.cpp berechnet den größten gemeinsamen Teiler zweier Zahlen.

// constexpr14.cpp

#include <iostream>

constexpr auto gcd(int a, int b){
while (b != 0){
auto t= b;
b= a % b;
a= t;
}
return a;
}

int main(){

std::cout << '\n';

constexpr int i= gcd(11, 121); // (1)

int a= 11;
int b= 121;
int j= gcd(a, b); // (2)

std::cout << "gcd(11,121): " << i << '\n';
std::cout << "gcd(a,b): " << j << '\n';

std::cout << '\n';

}

(1) berechnet das Ergebnis i zur Compilezeit und (2) j zur Runtime. Der Compiler würde sich beschweren, wenn ich j als constexpr deklariere: constexpr int j = gcd(a, b). Das Problem ist in diesem Fall, dass die Integer a und b keine konstanten Ausdrücke sind.

Die Ausgabe des Programms sollte nicht überraschen.

constexpr Funktionen

Die Überraschung kann jetzt beginnen. Die Magie zeige ich mit dem Compiler Explorer.

constexpr Funktionen

(1) im Programm constexpr14.cpp läuft auf die Konstante 11 im folgenden Ausdruck hinaus: mov DWORD PTR[rbp-4], 11 (Zeile 33 im Screenshot). Im Gegensatz dazu ist Zeile (2) ein Funktionsaufruf: call gcd(int, int) (Zeile 41 im Screenshot).

Nach dieser Zusammenfassung möchte ich auf die Gemeinsamkeiten von constexpr-Funktionen und Template-Metaprogrammierung eingehen.

constexpr-Funktionen haben viel mit der Template-Metaprogrammierung gemeinsam. Wer mit der Template-Metaprogrammierung nicht vertraut ist, sollte meine drei vorangegangenen Beiträge einen Eindruck vermitteln.

Hier ist das große Bild, das constexpr-Funktionen mit Template-Metaprogrammierung vergleicht:

constexpr Funktionen

Ich möchte meiner Tabelle noch ein paar Anmerkungen hinzufügen.

Zugegeben, dieser Vergleich war recht knapp. Ein bildlicher Vergleich einer Metafunktion (siehe Template-Metaprogrammierung - Wie es funktioniert [4]) und einer constexpr-Funktion sollte die offenen Fragen beantworten. Beide Funktionen berechnen die Fakultät einer Zahl.

constexpr Funktionen
constexpr Funktionen
constexpr Funktionen
constexpr Funktionen
constexpr Funktionen
constexpr Funktionen

constexpr-Funktionen und Templates haben aber noch mehr gemeinsam.

Details zur Template-Instantiierung finden sich in meinen vorherigen Beitrag "Template-Instanziierung [5]". Ich möchte hier nur die wichtigsten Fakten hervorheben.

Eine Template wie isSmaller wird zweimal syntaktisch geprüft:

template<typename T>
bool isSmaller(T fir, T sec){
return fir < sec;
}

isSmaller(5, 10); // (1)

std::unordered_set<int> set1;
std::unordered_set<int> set2;

constexpr-Funktionen werden ebenfalls zweimal auf ihre Syntax geprüft.

constexpr auto gcd(int a, int b){
while (b != 0){
auto t= b;
b= a % b;
a= t;
}
return a;
}


constexpr int i= gcd(11, 121); // (1)

int a= 11;
int b= 121;
constexpr int j= gcd(a, b); // (2)

Letztlich sind sich Templates und constexpr-Funktionen auch in Bezug auf die Sichtbarkeit ihrer Definition sehr ähnlich.

Zum Instanziieren eines Template muss dessen Definition sichtbar sein. Das Gleiche gilt für constexpr-Funktionen.

Im nächsten Beitrag schreibe ich über constexpr-Funktionen in C++20 und das neue C++20 Schlüsselwort consteval. ( [6])


URL dieses Artikels:
https://www.heise.de/-6342383

Links in diesem Artikel:
[1] https://www.heise.de/developer/artikel/Template-Metaprogrammierung-Wie-alles-begann-6233576.html
[2] https://www.heise.de/developer/artikel/Template-Metaprogrammierung-Wie-es-funktioniert-6237233.html
[3] https://www.heise.de/developer/artikel/Template-Metaprogrammierung-Hybride-Programmierung-6266012.html
[4] https://www.heise.de/developer/artikel/Template-Metaprogrammierung-Wie-es-funktioniert-6237233.html
[5] https://www.heise.de/developer/artikel/Template-Instanziierung-6151298.html
[6] mailto:rainer@grimm-jaud.de