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:

C ++ 11
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:

C ++ 11
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:

C ++ 11
constexpr int size = abs(10);

Om inte funktionen ( abs ) själv returnerar en constexpr .

Alla grundtyper kan initialiseras med constexpr .

C ++ 11
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";

Intressant och bekvämt kan du också använda auto :

C ++ 11
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å:

C ++ 11
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:

C ++ 11
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:

C ++ 11
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:

C ++ 11
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:

C ++ 11
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:

C ++ 11
constexpr int Sum(int a, int b)
{
   constexpr int a1 = a;     // ERROR
   ..

Dessutom kommer följande inte heller sammanställa:

C ++ 11
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?

C ++ 11
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

C ++ 17

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
}


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow