Sök…
Introduktion
constexpr
är ett nyckelord som kan användas för att markera en variabelns värde som ett konstant uttryck, en funktion som är potentiellt användbar i konstant uttryck, eller (sedan C ++ 17) ett if-uttal som endast har en av sina grenar valda att sammanställas.
Anmärkningar
constexpr
nyckelordet lades till i C ++ 11, men i några år sedan C ++ 11-standarden publicerades, stödde inte alla större kompilatorer det. vid tidpunkten då C ++ 11-standarden publicerades. Från tidpunkten för publicering av C ++ 14 stöder alla större kompilatorer constexpr
.
constexpr-variabler
En variabel deklarerad constexpr
är implicit const
och dess värde kan användas som ett konstant uttryck.
Jämförelse med #define
En constexpr
är typsäker ersättning för #define
baserade kompileringstidsuttryck. Med constexpr
den utvärderade kompileringstiden med resultatet. Till exempel:
int main()
{
constexpr int N = 10 + 2;
cout << N;
}
kommer att producera följande kod:
cout << 12;
Ett pre-processor baserat kompileringstid makro skulle vara annorlunda. Överväga:
#define N 10 + 2
int main()
{
cout << N;
}
kommer att producera:
cout << 10 + 2;
vilket uppenbarligen kommer att konverteras till cout << 10 + 2;
. Men kompilatorn måste göra mer arbete. Dessutom skapar det ett problem om det inte används korrekt.
Till exempel (med #define
):
cout << N * 2;
former:
cout << 10 + 2 * 2; // 14
Men en constexpr
skulle korrekt ge 24
.
Jämförelse med const
En const
variabel är en variabel som behöver minne för lagring. En constexpr
gör det inte. En constexpr
producerar kompileringstidskonstant, som inte kan ändras. Du kan hävda att const
inte heller kan ändras. Men tänk på:
int main()
{
const int size1 = 10;
const int size2 = abs(10);
int arr_one[size1];
int arr_two[size2];
}
Med de flesta kompilatorer kommer det andra uttalet att misslyckas (kan till exempel fungera med GCC). Storleken på valfri grupp, som du kanske vet, måste vara ett konstant uttryck (dvs. resulterar i kompileringstidsvärde). Den andra variabeln size2
tilldelas ett värde som bestäms vid körning (även om du vet att det är 10
, för kompilatorn är det inte kompileringstid).
Detta innebär att en const
kan eller inte är en verklig kompileringstidskonstant. Du kan inte garantera eller säkerställa att ett visst const
är absolut kompileringstid. Du kan använda #define
men det har sina egna fallgropar.
Använd därför helt enkelt:
int main()
{
constexpr int size = 10;
int arr[size];
}
Ett constexpr
uttryck måste utvärderas till ett kompileringstidsvärde. Således kan du inte använda:
constexpr int size = abs(10);
Om inte funktionen ( abs
) själv returnerar en constexpr
.
Alla grundtyper kan initialiseras med constexpr
.
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";
Intressant och bekvämt kan du också använda auto
:
constexpr auto domain = ".COM"; // const char * const domain = ".COM"
constexpr auto PI = 3.14; // constexpr double
constexpr-funktioner
En funktion som förklaras constexpr
är implicit inline och kallar till en sådan funktion som potentiellt ger konstant uttryck. Till exempel ger följande funktion, om den anropas med konstant uttrycksargument, ett konstant uttryck också:
constexpr int Sum(int a, int b)
{
return a + b;
}
Således kan resultatet av funktionssamtalet användas som en matrisbunden eller ett mallargument eller för att initialisera en constexpr
variabel:
int main()
{
constexpr int S = Sum(10,20);
int Array[S];
int Array2[Sum(20,30)]; // 50 array size, compile time
}
Observera att om du tar bort constexpr
från funktionens specifikation för constexpr
, kommer tilldelning till S
inte att fungera, eftersom S
är en constexpr
variabel och måste tilldelas en kompileringstidkonst. På liknande sätt kommer storleken på matrisen inte att vara ett konstant uttryck, om funktionen Sum
inte är constexpr
.
Intressant med constexpr
funktioner är att du också kan använda dem som vanliga funktioner:
int a = 20;
auto sum = Sum(a, abs(-20));
Sum
kommer inte att vara en constexpr
funktion nu, den kommer att sammanställas som en vanlig funktion, med variabla (icke-konstanta) argument och returnera icke-konstant värde. Du behöver inte skriva två funktioner.
Det betyder också att om du försöker tilldela ett sådant samtal till en icke-const-variabel kommer den inte att kompilera:
int a = 20;
constexpr auto sum = Sum(a, abs(-20));
Anledningen är enkel: constexpr
måste endast tilldelas en kompileringstidskonstant. Men ovanstående funktionssamtal gör Sum
ett icke- constexpr
(R-värde är icke-const, men L-värde förklarar sig själv att vara constexpr
).
constexpr
funktionen måste också returnera en kompileringstidskonstant. Följande kommer inte att sammanställa:
constexpr int Sum(int a, int b)
{
int a1 = a; // ERROR
return a + b;
}
Eftersom a1
är en icke-constexpr- variabel och förbjuder funktionen att vara en riktig constexpr
funktion. Att göra det constexpr
och tilldela det a
vilja fungerar inte heller - eftersom värdet på a
(inkommande parameter) ännu inte är känt:
constexpr int Sum(int a, int b)
{
constexpr int a1 = a; // ERROR
..
Dessutom kommer följande inte heller sammanställa:
constexpr int Sum(int a, int b)
{
return abs(a) + b; // or abs(a) + abs(b)
}
Eftersom abs(a)
inte är ett konstant uttryck (till och med abs(10)
kommer inte att fungera, eftersom abs
inte returnerar ett constexpr int
!
Hur är detta?
constexpr int Abs(int v)
{
return v >= 0 ? v : -v;
}
constexpr int Sum(int a, int b)
{
return Abs(a) + b;
}
Vi tillverkade vår egen Abs
funktion som är en constexpr
, och kroppen av Abs
inte heller bryta någon regel. På samtalssidan (inuti Sum
) utvärderas uttrycket till en constexpr
. Följaktligen kommer samtalet till Sum(-10, 20)
att vara ett kompileringstidskonstantuttryck som resulterar till 30
.
Statisk om uttalande
if constexpr
uttalandet kan användas för att villkorligt sammanställa kod. Villkoret måste vara ett konstant uttryck. Den gren som inte är vald raderas. Ett kasserat uttalande i en mall är inte instanserat. Till exempel:
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
}
Dessutom är variabler och funktioner som ODR använda endast inne kasserade uttalanden inte behöver definieras, och kasserade return
uttalanden används inte för funktions returtyp avdrag.
if constexpr
skiljer sig från #ifdef
. #ifdef
sammanställer villkorligt kod, men bara baserat på villkor som kan utvärderas vid förbehandlingstid. Till exempel kunde #ifdef
inte användas för att villkorligt sammanställa kod beroende på värdet på en mallparameter. Å andra sidan, if constexpr
inte kan användas för att kassera syntaktisk ogiltig kod, medan #ifdef
kan.
if constexpr(false) {
foobar; // error; foobar has not been declared
std::vector<int> v("hello, world"); // error; no matching constructor
}