Dela via


constexpr (C++)

Nyckelordet constexpr introducerades i C++11 och förbättrades i C++14. Det innebär konstant uttryck. Precis som constkan den tillämpas på variabler: Ett kompilatorfel utlöses när någon kod försöker ändra värdet. Till skillnad från constkan constexpr också tillämpas på funktioner och klasskonstruktorer. constexpr anger att värdet, eller returvärdet, är konstant och om möjligt beräknas vid kompileringstillfället.

Ett constexpr integralvärde kan användas där ett const heltal krävs, till exempel i mallargument och matrisdeklarationer. Och när ett värde beräknas vid kompileringstid i stället för körning hjälper det ditt program att köras snabbare och använda mindre minne.

För att begränsa komplexiteten för kompileringstidskonstanta beräkningar och deras potentiella inverkan på kompileringstiden kräver C++14-standarden att typerna i konstanta uttryck är literaltyper.

Syntax

constexpr identifierare av literaltyp=konstantuttryck;
constexpr identifierare av literaltyp{konstantuttryck};
constexpr identifierare av literaltyp(params);
constexpr ctor(params);

Parametrar

params
En eller flera parametrar som var och en måste vara en literaltyp och måste vara ett konstant uttryck.

Returvärde

En constexpr variabel eller funktion måste returnera ett literalt värde av typen.

constexpr variabler

Den primära skillnaden mellan const och constexpr variabler är att initieringen av en const variabel kan skjutas upp till körningstiden. En constexpr variabel måste initieras vid kompileringen. Alla constexpr variabler är const.

  • En variabel kan deklareras med constexpr, när den har en literaltyp och initieras. Om initieringen utförs av en konstruktor måste konstruktorn deklareras som constexpr.

  • En referens kan deklareras som constexpr när båda dessa villkor uppfylls: Det refererade objektet initieras av ett konstant uttryck, och eventuella implicita konverteringar som anropas under initieringen är också konstanta uttryck.

  • Alla deklarationer av en constexpr variabel eller funktion måste ha constexpr-specificeraren.

constexpr float x = 42.0;
constexpr float y{108};
constexpr float z = exp(5, 3);
constexpr int i; // Error! Not initialized
int j = 0;
constexpr int k = j + 1; //Error! j not a constant expression

constexpr funktioner

En constexpr-funktion är en funktion vars returvärde kan beräknas vid kompileringstid när det efterfrågas av koden. Användning av kod kräver returvärdet vid kompileringstillfället för att initiera en constexpr-variabel eller för att tillhandahålla ett icke-typ mallargument. När dess argument är constexpr värden skapar en constexpr-funktion en kompileringskonstant. När det anropas med argument som inteconstexpr, eller när dess värde inte krävs vid kompileringstillfället, genererar det ett värde vid körning som en vanlig funktion. (Det här dubbla beteendet sparar dig från att behöva skriva constexpr och icke-constexpr versioner av samma funktion.)

En constexpr funktion eller konstruktor är implicit inline.

Följande regler gäller för constexpr funktioner:

  • En constexpr-funktion måste endast acceptera och returnera literaltyper.

  • En constexpr funktion kan vara rekursiv.

  • Före C++20 kan en constexpr-funktion inte vara virtuella, och en konstruktor kan inte definieras som constexpr när den omslutande klassen har några virtuella basklasser. I C++20 och senare kan en constexpr funktion vara virtuell. Visual Studio 2019 version 16.10 och senare versioner stöder constexpr virtuella funktioner när du anger alternativet /std:c++20 eller senare kompilator.

  • Kroppen kan definieras som = default eller = delete.

  • Textkroppen får inte innehålla några goto-instruktioner eller try-block.

  • En explicit specialisering av en mall som inte ärconstexpr kan deklareras som constexpr:

  • En explicit specialisering av en constexpr mall behöver inte heller vara constexpr:

Följande regler gäller för constexpr funktioner i Visual Studio 2017 och senare:

  • Den kan innehålla if- och switch-uttryck samt alla loop-satser, inklusive for, intervallbaserade for, whileoch do-while.

  • Den kan innehålla lokala variabeldeklarationer, men variabeln måste initieras. Det måste vara en literaltyp och får inte vara static eller trådlokal. Den lokalt deklarerade variabeln måste inte vara constoch kan mutera.

  • En constexpr icke-static medlemsfunktion behöver inte vara implicit const.

constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1) / 2) * x;
}

Tips

När du felsöker en icke-optimerad felsökningsversion i Visual Studio-felsökningsprogrammet kan du se om en constexpr funktion utvärderas vid kompileringstidpunkten genom att placera en brytpunkt i den. Om brytpunkten träffas anropades funktionen under körning. Annars anropades funktionen vid kompileringstillfället.

extern constexpr

Alternativet /Zc:externConstexpr kompilator gör att kompilatorn tillämpar extern länkning på variabler som deklareras med hjälp av extern constexpr. I tidigare versioner av Visual Studio, antingen som standard eller när /Zc:externConstexpr– anges, tillämpar Visual Studio intern länkning på constexpr variabler även när nyckelordet extern används. Alternativet /Zc:externConstexpr är tillgängligt från och med Visual Studio 2017 Update 15.6 och är inaktiverat som standard. Alternativet /permissive- aktiverar inte /Zc:externConstexpr.

Exempel

I följande exempel visas constexpr variabler, funktioner och en användardefinierad typ. I den sista instruktionen i main()är funktionen constexpr medlem GetValue() ett körningsanrop eftersom värdet inte krävs för att vara känt vid kompileringstillfället.

// constexpr.cpp
// Compile with: cl /EHsc /W4 constexpr.cpp
#include <iostream>

using namespace std;

// Pass by value
constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1) / 2) * x;
}

// Pass by reference
constexpr float exp2(const float& x, const int& n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp2(x * x, n / 2) :
        exp2(x * x, (n - 1) / 2) * x;
}

// Compile-time computation of array length
template<typename T, int N>
constexpr int length(const T(&)[N])
{
    return N;
}

// Recursive constexpr function
constexpr int fac(int n)
{
    return n == 1 ? 1 : n * fac(n - 1);
}

// User-defined type
class Foo
{
public:
    constexpr explicit Foo(int i) : _i(i) {}
    constexpr int GetValue() const
    {
        return _i;
    }
private:
    int _i;
};

int main()
{
    // foo is const:
    constexpr Foo foo(5);
    // foo = Foo(6); //Error!

    // Compile time:
    constexpr float x = exp(5, 3);
    constexpr float y { exp(2, 5) };
    constexpr int val = foo.GetValue();
    constexpr int f5 = fac(5);
    const int nums[] { 1, 2, 3, 4 };
    const int nums2[length(nums) * 2] { 1, 2, 3, 4, 5, 6, 7, 8 };

    // Run time:
    cout << "The value of foo is " << foo.GetValue() << endl;
}

Krav

Visual Studio 2015 eller senare.

Se även

Deklarationer och definitioner
const