Zoeken…
Invoering
constexpr
is een sleutelwoord dat kan worden gebruikt om de waarde van een variabele te markeren als een constante expressie, een functie die mogelijk bruikbaar is in constante expressies, of (sinds C ++ 17) een if-instructie waarbij slechts één van de vertakkingen is geselecteerd om te worden gecompileerd.
Opmerkingen
Het sleutelwoord constexpr
is toegevoegd in C ++ 11, maar sinds de publicatie van de C ++ 11-standaard een paar jaar lang werd dit niet door alle grote compilers ondersteund. op het moment dat de C ++ 11-standaard werd gepubliceerd. Vanaf het moment van publicatie van C ++ 14 ondersteunen alle belangrijke compilers constexpr
.
constexpr-variabelen
Een variabele die constexpr
wordt constexpr
is impliciet const
en de waarde ervan kan als een constante uitdrukking worden gebruikt.
Vergelijking met #define
Een constexpr
is een constexpr
vervanging voor op #define
gebaseerde compile-time expressies. Met constexpr
de constexpr
de compilatie geëvalueerde expressie vervangen door het resultaat. Bijvoorbeeld:
int main()
{
constexpr int N = 10 + 2;
cout << N;
}
zal de volgende code produceren:
cout << 12;
Een op de processor gebaseerde compilatie-macro zou anders zijn. Overwegen:
#define N 10 + 2
int main()
{
cout << N;
}
zal produceren:
cout << 10 + 2;
die uiteraard wordt geconverteerd naar cout << 10 + 2;
. De compiler zou echter meer werk moeten doen. Het creëert ook een probleem als het niet correct wordt gebruikt.
Bijvoorbeeld (met #define
):
cout << N * 2;
vormen:
cout << 10 + 2 * 2; // 14
Maar een vooraf geëvalueerde constexpr
zou correct 24
geven.
Vergelijking met const
Een const
variabele is een variabele die geheugen nodig heeft voor de opslag ervan. Een constexpr
doet dat niet. Een constexpr
produceert compileertijdconstante, die niet kan worden gewijzigd. Je zou kunnen beweren dat const
ook niet mag worden gewijzigd. Maar overweeg:
int main()
{
const int size1 = 10;
const int size2 = abs(10);
int arr_one[size1];
int arr_two[size2];
}
Bij de meeste compilers mislukt de tweede instructie (kan bijvoorbeeld met GCC werken). De grootte van elke array moet, zoals u misschien weet, een constante expressie zijn (dat wil zeggen resulteert in compilatie-tijdwaarde). Aan de tweede variabele size2
wordt een waarde toegewezen die tijdens runtime wordt bepaald (hoewel u weet dat het 10
, is het voor de compiler geen compilatie).
Dit betekent dat een const
al dan niet een echte compilatie-tijdconstante kan zijn. U kunt niet garanderen of afdwingen dat een bepaalde const
waarde absoluut compilatie is. U kunt #define
maar het heeft zijn eigen valkuilen.
Gebruik daarom gewoon:
int main()
{
constexpr int size = 10;
int arr[size];
}
Een constexpr
expressie moet de waarde compileren. U kunt dus niet gebruiken:
constexpr int size = abs(10);
Tenzij de functie ( abs
) zelf een constexpr
.
Alle basistypen kunnen worden geïnitialiseerd met constexpr
.
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";
Interessant en handig kunt u ook auto
:
constexpr auto domain = ".COM"; // const char * const domain = ".COM"
constexpr auto PI = 3.14; // constexpr double
constexpr-functies
Een functie die constexpr
wordt verklaard constexpr
is impliciet inline en roept een dergelijke functie op die mogelijk constante expressies constexpr
. De volgende functie levert bijvoorbeeld, indien aangeroepen met argumenten voor constante expressie, ook een constante expressie op:
constexpr int Sum(int a, int b)
{
return a + b;
}
Het resultaat van de functieaanroep kan dus worden gebruikt als een array-gebonden of een sjabloonargument, of om een constexpr
variabele te initialiseren:
int main()
{
constexpr int S = Sum(10,20);
int Array[S];
int Array2[Sum(20,30)]; // 50 array size, compile time
}
Merk op dat als u constexpr
uit de specificatie van het constexpr
van de functie verwijdert, toewijzing aan S
niet zal werken, aangezien S
een constexpr
variabele is en een compile-time const moet krijgen. Evenzo zal de grootte van de array ook geen constante-expressie zijn, als de functie Sum
niet constexpr
.
Interessant aan constexpr
functies is dat u het ook kunt gebruiken als gewone functies:
int a = 20;
auto sum = Sum(a, abs(-20));
Sum
zal nu geen constexpr
functie zijn, het wordt gecompileerd als een gewone functie, waarbij variabele (niet-constante) argumenten worden gebruikt en niet-constante waarde wordt constexpr
. U hoeft niet twee functies te schrijven.
Het betekent ook dat als u een dergelijke aanroep probeert toe te wijzen aan een niet-const variabele, deze niet zal compileren:
int a = 20;
constexpr auto sum = Sum(a, abs(-20));
De reden is simpel: aan constexpr
moet alleen een compilatie-tijdconstante worden toegewezen. De bovenstaande functieaanroep maakt Sum
een niet- constexpr
(R-waarde is niet-const, maar L-waarde verklaart zichzelf constexpr
).
De constexpr
functie moet ook een compilatie-tijdconstante retourneren. Volgende compileert niet:
constexpr int Sum(int a, int b)
{
int a1 = a; // ERROR
return a + b;
}
Omdat a1
een niet-constexpr- variabele is en verbiedt dat de functie een echte constexpr
functie is. Daarom is constexpr
en het toewijzen van a
zal evenmin work - aangezien waarde van a
(inkomend parameter) is nog steeds niet bekend:
constexpr int Sum(int a, int b)
{
constexpr int a1 = a; // ERROR
..
Verder zullen de volgende ook niet compileren:
constexpr int Sum(int a, int b)
{
return abs(a) + b; // or abs(a) + abs(b)
}
Omdat abs(a)
geen constante uitdrukking is (zelfs abs(10)
zal niet werken, aangezien abs
geen constexpr int
retourneert!
Hoe zit het met dit?
constexpr int Abs(int v)
{
return v >= 0 ? v : -v;
}
constexpr int Sum(int a, int b)
{
return Abs(a) + b;
}
We hebben onze eigen Abs
functie gemaakt, die een constexpr
, en het lichaam van Abs
breekt ook geen enkele regel. Op de oproepsite (in Sum
) wordt de uitdrukking ook geëvalueerd als een constexpr
. Daarom zal de aanroep naar Sum(-10, 20)
een compilatie-tijd constante expressie zijn die resulteert in 30
.
Statische if-verklaring
De instructie if constexpr
kan worden gebruikt om code voorwaardelijk te compileren. De voorwaarde moet een constante uitdrukking zijn. De vertakking die niet is geselecteerd, wordt verwijderd. Een weggegooide verklaring in een sjabloon wordt niet geïnstantieerd. Bijvoorbeeld:
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
}
Bovendien worden variabelen en functies die worden gebruikt ODR alleen binnen weggegooid verklaringen niet te worden gedefinieerd en weggegooid return
verklaringen niet gebruikt functieretourneringstype aftrek.
if constexpr
verschilt van #ifdef
. #ifdef
compileert voorwaardelijk code, maar alleen op basis van voorwaarden die tijdens de voorbewerking kunnen worden geëvalueerd. Afhankelijk van de waarde van een sjabloonparameter kan #ifdef
bijvoorbeeld niet worden gebruikt om code voorwaardelijk te compileren. Aan de andere kant, if constexpr
niet kan worden gebruikt om syntactisch ongeldige code weg te gooien, terwijl #ifdef
kan.
if constexpr(false) {
foobar; // error; foobar has not been declared
std::vector<int> v("hello, world"); // error; no matching constructor
}