zurück zum Artikel

constexpr if

Rainer Grimm

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

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.

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:

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).

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.

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

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