constexpr if
Im heutigen Artikel möchte ich ein sehr interessantes C++17-Feature vorstellen: constexpr if. Es ermöglicht, Sourcecode bedingt zu kompilieren und kann auch für nette Tricks zur Compilezeit verwendet werden.
Im heutigen Artikel möchte ich ein sehr interessantes C++17-Feature vorstellen: constexpr if.
Es ermöglicht , Sourcecode bedingt zu kompilieren und kann auch für nette Tricks zur Compilezeit verwendet werden.
![constexpr if](https://heise.cloudimg.io/width/696/q85.png-lossy-85.webp-lossy-85.foil1/_www-heise-de_/imgs/18/3/3/0/1/6/1/3/constexprIf-f8924197d33f5133.png)
Die Einführung von constexpr if
ist recht einfach.
template <typename T>
auto getValue(T t) {
if constexpr (std::is_pointer_v<T>) // (1)
return *t; // deduces return type to int for T = int*
else // (2)
return t; // deduces return type to int for T = int
}
Der Codeschnipsel zeigt eine interessante Eigenschaft von constexpr if
: Obwohl es constexpr if
genannt wird, wird es als if constexpr
verwendet: if constexpr (std::is_pointer_v<T>)
.
Wenn T
ein Zeiger ist, wird der if
-Zweig in (1) kompiliert. Wenn nicht, wird der else
-Zweig in (2) kompiliert. Zwei Punkte sind wichtig zu erwähnen: Die Funktion getValue
hat zwei verschiedene Rückgabetypen und beide Zweige der if-
Anweisung müssen gültig sein.
Der Ausdruck in constexpr if
muss ein Compilezeitprädikat sein: eine Funktion, die einen booleschen Wert zurückgibt und zur Compilezeit ausgeführt wird. Ich habe im Codeschnipsel eine Funktion aus der type-traits-Bibliothek verwendet. Alternativ lässt sich in C++20 auch ein Concept verwenden. Hier ist das entsprechende Beispiel, das das Concept std::integral
einsetzt:
template <typename T>
auto get_value(T t) {
if constexpr (std::integral<T>) // (1)
return *t; // deduces return type to int for T = int*
else // (2)
return t; // deduces return type to int for T = int
}
Okay, die beiden Codeschnipsel sind nicht sehr beeindruckend. Daher gehe ich über zur Template-Metaprogrammierung.
Dank constexpr if
ist Template-Metaprogrammierung oft einfacher zu schreiben und zu lesen.
Template-Metaprogrammierung mit constexpr if
Metaprogrammierung ist Programmieren auf Programmen. C++ wendet Metaprogrammierung zur Compilezeit an. Sie begann in C++98 mit der Template-Metaprogrammierung, wurde in C++11 mit der type-traits-Bibliothek formalisiert und hat sich seitdemstetig verbessert.
Hier ist das "Hello World" der Template-Metaprogrammierung: Berechnen der Fakultät einer Zahl:
// factorial.cpp
#include <iostream>
template <int N> // (2)
struct Factorial{
static int const value = N * Factorial<N-1>::value;
};
template <> // (3)
struct Factorial<1>{
static int const value = 1;
};
int main(){
std::cout << '\n';
std::cout << "Factorial<5>::value: "
<< Factorial<5>::value << '\n' ; // (1)
std::cout << "Factorial<10>::value: "
<< Factorial<10>::value << '\n' ; // (4)
std::cout << '\n' ;
}
Der Aufruf factorial<5>::value
(1) bewirkt die Instanziierung des primären oder allgemeinen Template (2). Während dieser Instanziierung wird Factorial<4>::value
instanziiert. Diese rekusrive Instanziierung endet mit dem vollständig spezialisierte Klassen-Template Factorial<1>
(Zeile 3).
Mehr über Template-Metaprogrammierung findet sich in meinen früheren Artikeln:
- Template-Metaprogrammierung: Wie alles anfing [1]
- Template-Metaprogrammierung: Wie es funktioniert [2]
- Template-Metaprogrammierung: Hybride Programmierung [3]
Nun schreibe ich das Programm mithilfe von constexpr if
um:
// factorialConstexprIf.cpp
template <int N> // (1)
struct Factorial{
static int const value = N * Factorial<N-1>::value;
};
template <> // (2)
struct Factorial<1>{
static int const value = 1;
};
template <int N> // (3)
constexpr int factorial() {
if constexpr (N >= 2)
return N * factorial<N-1>();
else
return N;
}
int main(){
static_assert(Factorial<5>::value == factorial<5>()); // (4)
static_assert(Factorial<10>::value == factorial<10>()); // (4)
}
Das primäre Template von Factorial
(Zeile 1) wird zur if
-Bedingung in der constexpr
-Funktion factorial
(3), und die vollständige Spezialisierung von Factorial
für 1 (2) wird zum else
-Fall in der constexpr
-Funktion factorial
(3). Natürlich geben die Klassen-Templates Factorial
und die constexpr-Funktion factorial
das gleiche Ergebnis zurück und werden zur Compilezeit ausgeführt (4). Um es kurz zu machen: Ich bevorzuge die constexpr
Funktion mit constexpr if,
weil sie sich so angenehm liest wie eine normale Funktion.
Zum Abschluss möchte ich noch ein weiteres Beispiel zeigen: die berüchtigte Fibonacci-Funktion. Die folgenden Implementierungen basieren auf Template-Metaprogrammierung (Fibonacci
) und constexpr if (fibonacci).
// fibonacciConstexprIf.cpp
template<int N>
constexpr int fibonacci()
{
if constexpr (N>=2)
return fibonacci<N-1>() + fibonacci<N-2>();
else
return N;
}
template <int N> // (1)
struct Fibonacci{
static int const value =
Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};
template <> // (2)
struct Fibonacci<1>{
static int const value = 1;
};
template <> // (3)
struct Fibonacci<0>{
static int const value = 0;
};
int main() {
static_assert(fibonacci<7>() == 13);
static_assert(fibonacci<7>() == Fibonacci<7>::value);
}
Die constexpr
-Funktion fibonacci
ist sehr angenehm zu lesen. Die gesamte Funktionalität befindet sich in einem einzigen Funktionskörper. Im Gegensatz dazu benötigt das Template-Metaprogramm Fibonacci drei Klassen. Das primäre Template (1) und die beiden vollständigen Spezialisierungen für die Werte 1 und 0 (2 und 3).
More Information about my Mentoring Program "Fundamentals for C++ Professionals"
I created the platform for my new mentoring on https://www.modernescpp.org/ [4]. You can skip through each of the 28 lessons. I also presented the 6th lesson about move semantics and perfect forwarding in the post 'More Information about my Mentoring Program "Fundamentals for C++ Professionals"' [5]. Here are the next step before I start the mentoring program.
- Beginning of March: online information session about my mentoring program, where you can also ask your questions
- Middle of March: my mentoring program opens for registration
- April: the registration for the mentoring program closes, and the mentoring program starts
If you want to stay informed, write an e-mail to info@ModernesCpp.de with the subject "Mentoring". Write me also an e-mail if you need more information
Wie geht's weiter?
Templates sind ein mächtiges Werkzeug und bieten daher neue Möglichkeiten für den Softwareentwurf. In meinem nächsten Artikel schreibe ich über statische und dynamische Polymorphie. ( [6])
URL dieses Artikels:
https://www.heise.de/-6451955
Links in diesem Artikel:
[1] https://heise.de/-6233576
[2] https://heise.de/-6237233
[3] https://heise.de/-6266012
[4] https://www.modernescpp.org/
[5] https://www.modernescpp.org/moredetails/
[6] mailto:rainer@grimm-jaud.de
Copyright © 2022 Heise Medien