Suche…
Einführung
constexpr
ist ein Schlüsselwort , das verwendet werden kann, um den Wert einer Variablen als konstanten Ausdruck zu kennzeichnen, eine Funktion, die möglicherweise in konstanten Ausdrücken verwendet werden kann, oder (seit C ++ 17) eine if-Anweisung , bei der nur einer ihrer Zweige zum Kompilieren ausgewählt wurde.
Bemerkungen
Das Schlüsselwort constexpr
wurde in C ++ 11 hinzugefügt, aber seit der Veröffentlichung des C ++ 11-Standards wurde es nicht von allen großen Compilern unterstützt. zu der Zeit, als der C ++ 11-Standard veröffentlicht wurde. Zum Zeitpunkt der Veröffentlichung von C ++ 14 unterstützen alle großen Compiler constexpr
.
Constexpr-Variablen
Eine deklarierte constexpr
ist implizit const
und ihr Wert kann als konstanter Ausdruck verwendet werden.
Vergleich mit #define
Ein constexpr
ist typsichere Ersatz für #define
basierte Kompilierung-Ausdrücke. Mit constexpr
der zur Kompilierzeit ausgewertete Ausdruck durch das Ergebnis ersetzt. Zum Beispiel:
int main()
{
constexpr int N = 10 + 2;
cout << N;
}
erzeugt den folgenden Code:
cout << 12;
Ein vorprozessorgestütztes Kompilierungszeitmakro würde sich unterscheiden. Erwägen:
#define N 10 + 2
int main()
{
cout << N;
}
wird herstellen:
cout << 10 + 2;
das wird offensichtlich in cout << 10 + 2;
. Der Compiler müsste jedoch mehr Arbeit erledigen. Es wird auch ein Problem erstellt, wenn es nicht korrekt verwendet wird.
Zum Beispiel (mit #define
):
cout << N * 2;
Formen:
cout << 10 + 2 * 2; // 14
Ein vorher ausgewertetes constexpr
würde jedoch 24
.
Vergleich mit const
Eine const
Variable ist eine Variable, die zum Speichern Speicher benötigt. Ein constexpr
nicht. Ein constexpr
erzeugt eine Kompilierungszeitkonstante, die nicht geändert werden kann. Sie können argumentieren, dass const
auch nicht geändert werden darf. Aber bedenken Sie:
int main()
{
const int size1 = 10;
const int size2 = abs(10);
int arr_one[size1];
int arr_two[size2];
}
Bei den meisten Compilern schlägt die zweite Anweisung fehl (funktioniert beispielsweise mit GCC). Die Größe eines Arrays muss, wie Sie vielleicht wissen, ein konstanter Ausdruck sein (dh er führt zu einem Kompilierzeitwert). Der zweiten Variablen size2
wird ein Wert zugewiesen, der zur Laufzeit festgelegt wird (obwohl Sie wissen, dass es 10
, ist dies für den Compiler keine Kompilierzeit).
Dies bedeutet, dass ein const
eine echte Konstante für die Kompilierungszeit sein kann oder nicht. Sie können nicht garantieren oder erzwingen, dass ein bestimmter const
Wert die Kompilierungszeit ist. Sie können #define
, hat aber eigene Fallstricke.
Verwenden Sie dazu einfach:
int main()
{
constexpr int size = 10;
int arr[size];
}
Ein constexpr
Ausdruck muss zu einem Kompilierungszeitwert ausgewertet werden. Daher können Sie nicht verwenden:
constexpr int size = abs(10);
Es sei denn, die Funktion ( abs
) constexpr
selbst ein constexpr
.
Alle constexpr
können mit constexpr
initialisiert constexpr
.
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";
Interessanterweise und bequemerweise können Sie auch auto
:
constexpr auto domain = ".COM"; // const char * const domain = ".COM"
constexpr auto PI = 3.14; // constexpr double
Constexpr-Funktionen
Eine Funktion, die als constexpr
deklariert constexpr
ist implizit inline und Aufrufe einer solchen Funktion führen möglicherweise zu konstanten Ausdrücken. Wenn die folgende Funktion beispielsweise mit Argumenten für konstante Ausdrücke aufgerufen wird, ergibt sich auch ein konstanter Ausdruck:
constexpr int Sum(int a, int b)
{
return a + b;
}
Daher kann das Ergebnis des Funktionsaufrufs als Array-gebundenes oder Template-Argument verwendet werden oder eine constexpr
Variable initialisieren:
int main()
{
constexpr int S = Sum(10,20);
int Array[S];
int Array2[Sum(20,30)]; // 50 array size, compile time
}
Wenn Sie constexpr
aus der Rückgabetypspezifikation der Funktion entfernen, funktioniert die Zuweisung an S
nicht, da S
eine constexpr
Variable ist und eine constexpr
zur Kompilierzeit zugewiesen werden muss. In ähnlicher Weise ist auch die Größe des Arrays kein Konstantenausdruck, wenn die Funktion Sum
nicht constexpr
.
Interessant an den constexpr
Funktionen ist, dass Sie sie auch wie gewöhnliche Funktionen verwenden können:
int a = 20;
auto sum = Sum(a, abs(-20));
Sum
ist jetzt keine constexpr
Funktion, sondern wird als gewöhnliche Funktion kompiliert, wobei variable (nicht konstante) Argumente verwendet werden und ein nicht konstanter Wert zurückgegeben wird. Sie müssen nicht zwei Funktionen schreiben.
Das bedeutet auch, dass wenn Sie versuchen, einen solchen Aufruf einer Nicht-const-Variablen zuzuweisen, diese nicht kompiliert wird:
int a = 20;
constexpr auto sum = Sum(a, abs(-20));
Der Grund ist einfach: constexpr
muss nur eine Kompilierungszeitkonstante zugewiesen werden. Der obige Funktionsaufruf macht Sum
einem nicht- constexpr
(R-Wert ist nicht-const, aber der L-Wert deklariert sich selbst zu constexpr
).
Die Funktion constexpr
muss auch eine Konstante für die Kompilierungszeit zurückgeben. Folgendes wird nicht kompilieren:
constexpr int Sum(int a, int b)
{
int a1 = a; // ERROR
return a + b;
}
Weil a1
eine Nicht-Constexpr- Variable ist und verhindert, dass die Funktion eine echte constexpr
Funktion ist. So dass es constexpr
und Zuweisen a
auch nicht - da Wert a
(incoming Parameter) noch noch nicht bekannt ist:
constexpr int Sum(int a, int b)
{
constexpr int a1 = a; // ERROR
..
Außerdem wird Folgendes nicht kompiliert:
constexpr int Sum(int a, int b)
{
return abs(a) + b; // or abs(a) + abs(b)
}
Da abs(a)
kein konstanter Ausdruck ist (auch abs(10)
funktioniert nicht, da abs
kein constexpr int
!
Was ist damit?
constexpr int Abs(int v)
{
return v >= 0 ? v : -v;
}
constexpr int Sum(int a, int b)
{
return Abs(a) + b;
}
Wir haben unsere eigene Abs
Funktion entwickelt, die ein constexpr
, und der Körper von Abs
constexpr
auch keine Regel. Auch an der Aufrufstelle (innerhalb von Sum
) wird der Ausdruck zu einem constexpr
ausgewertet. Daher ist der Aufruf von Sum(-10, 20)
ein konstanter Ausdruck der Kompilierungszeit, der sich zu 30
ergibt.
Statische if-Anweisung
Die if constexpr
Anweisung kann verwendet werden, um Code bedingt zu kompilieren. Die Bedingung muss ein konstanter Ausdruck sein. Der nicht ausgewählte Zweig wird verworfen. Eine verworfene Anweisung in einer Vorlage wird nicht instanziiert. Zum Beispiel:
template<class T, class ... Rest>
void g(T &&p, Rest &&...rs)
{
// ... handle p
if constexpr (sizeof...(rs) > 0)
g(rs...); // never instantiated with an empty argument list
}
Außerdem müssen Variablen und Funktionen, die nur in verworfenen Anweisungen verwendet werden, nicht definiert werden, und verworfene return
werden nicht für die Ableitung von Funktionstypen verwendet.
if constexpr
von #ifdef
. #ifdef
kompiliert Code bedingt, jedoch nur basierend auf Bedingungen, die zum Zeitpunkt der Vorverarbeitung ausgewertet werden können. Beispielsweise kann #ifdef
nicht verwendet werden, um Code abhängig vom Wert eines Vorlagenparameters bedingt zu kompilieren. if constexpr
nicht dazu verwendet werden kann, syntaktisch ungültigen Code zu verwerfen, kann dies mit #ifdef
.
if constexpr(false) {
foobar; // error; foobar has not been declared
std::vector<int> v("hello, world"); // error; no matching constructor
}