Buscar..


Introducción

constexpr es una palabra clave que se puede usar para marcar el valor de una variable como una expresión constante, una función como potencialmente utilizable en expresiones constantes, o (desde C ++ 17) una declaración if que tiene solo una de sus ramas seleccionadas para compilar.

Observaciones

La palabra clave constexpr se agregó en C ++ 11, pero desde hace algunos años desde que se publicó el estándar C ++ 11, no todos los compiladores principales lo admitieron. En el momento en que se publicó el estándar C ++ 11. En el momento de la publicación de C ++ 14, todos los compiladores principales admiten constexpr .

variables constexpr

Una variable declarada constexpr es implícitamente const y su valor puede usarse como una expresión constante.

Comparación con #define

Un constexpr es un reemplazo de tipo seguro para expresiones de tiempo de compilación basadas en #define . Con constexpr la expresión evaluada en tiempo de compilación se reemplaza con el resultado. Por ejemplo:

C ++ 11
int main()
{
   constexpr int N = 10 + 2;
   cout << N;
}

producirá el siguiente código:

cout << 12;

Una macro en tiempo de compilación basada en un preprocesador sería diferente. Considerar:

#define N 10 + 2

int main()
{
    cout << N;
}

Producirá:

cout << 10 + 2;

que obviamente se convertirá en cout << 10 + 2; . Sin embargo, el compilador tendría que hacer más trabajo. Además, crea un problema si no se utiliza correctamente.

Por ejemplo (con #define ):

cout << N * 2;

formas:

cout << 10 + 2 * 2; // 14

Pero un constexpr daría correctamente 24 .

Comparacion con const

Una variable const es una variable que necesita memoria para su almacenamiento. Un constexpr no lo hace. Un constexpr produce una constante de tiempo de compilación, que no se puede cambiar. Puedes argumentar que la const puede también ser cambiada. Pero considere:

int main()
{
   const int size1 = 10;
   const int size2 = abs(10);

   int arr_one[size1]; 
   int arr_two[size2]; 
}

Con la mayoría de los compiladores, la segunda instrucción fallará (puede funcionar con GCC, por ejemplo). El tamaño de cualquier matriz, como usted sabe, tiene que ser una expresión constante (es decir, resultados en valor de tiempo de compilación). A la segunda variable size2 se le asigna algún valor que se decide en tiempo de ejecución (aunque sabe que es 10 , para el compilador no es tiempo de compilación).

Esto significa que una const puede o no ser una verdadera constante de compilación. No puede garantizar ni hacer cumplir que un valor const particular es absolutamente tiempo de compilación. Puedes usar #define pero tiene sus propios escollos.

Por lo tanto simplemente use:

C ++ 11
int main()
{
    constexpr int size = 10;

    int arr[size];
}

Una expresión constexpr debe evaluar un valor en tiempo de compilación. Por lo tanto, no puede utilizar:

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

A menos que la función ( abs ) esté devolviendo un constexpr .

Todos los tipos básicos se pueden inicializar con constexpr .

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

De manera interesante y conveniente, también puede usar auto :

C ++ 11
constexpr auto domain = ".COM";  // const char * const domain = ".COM"
constexpr auto PI = 3.14;        // constexpr double

funciones constexpr

Una función que se declara constexpr está implícitamente en línea y las llamadas a dicha función potencialmente producen expresiones constantes. Por ejemplo, la siguiente función, si se llama con argumentos de expresión constante, también produce una expresión constante:

C ++ 11
constexpr int Sum(int a, int b)
{
    return a + b;
}

Por lo tanto, el resultado de la llamada a la función se puede usar como una matriz enlazada o un argumento de plantilla, o para inicializar una variable constexpr :

C ++ 11
int main()
{
    constexpr int S = Sum(10,20);
   
    int Array[S];
    int Array2[Sum(20,30)]; // 50 array size, compile time
}

Tenga en cuenta que si elimina constexpr de la especificación de tipo de retorno de la función, la asignación a S no funcionará, ya que S es una variable constexpr , y debe asignarse una constante de tiempo de compilación. De manera similar, el tamaño de la matriz tampoco será una expresión constante, si la función Sum no es constexpr .

Lo interesante de constexpr funciones constexpr es que también puede usarlo como funciones comunes:

C ++ 11
int a = 20;
auto sum = Sum(a, abs(-20));

Sum no será una función constexpr ahora, se compilará como una función ordinaria, tomando argumentos variables (no constantes) y devolviendo un valor no constante. No necesitas escribir dos funciones.

También significa que si intenta asignar dicha llamada a una variable no constante, no se compilará:

C ++ 11
int a = 20;
constexpr auto sum = Sum(a, abs(-20));

La razón es simple: a constexpr solo se le debe asignar una constante de tiempo de compilación. Sin embargo, la llamada a la función anterior hace que Sum no sea constexpr (el valor R es no const, pero el valor L se declara a sí mismo como constexpr ).


La función constexpr también debe devolver una constante de tiempo de compilación. Lo siguiente no se compilará:

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

Porque a1 es una variable que no es constexpr, y prohíbe que la función sea una verdadera función constexpr . constexpr y asignarle a testamento tampoco funcionará, ya que aún no se conoce el valor de a (parámetro entrante):

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

Además, lo siguiente tampoco compilará:

C ++ 11
constexpr int Sum(int a, int b)
{
   return abs(a) + b; // or abs(a) + abs(b)
}

Dado que abs(a) no es una expresión constante (incluso abs(10) no funcionará, ya que abs no devuelve constexpr int !

Que hay de esto

C ++ 11
constexpr int Abs(int v)
{
    return v >= 0 ? v : -v;
}

constexpr int Sum(int a, int b)
{
    return Abs(a) + b;
}

Creamos nuestra propia función Abs , que es un constexpr , y el cuerpo de Abs tampoco rompe ninguna regla. Además, en el sitio de llamada (dentro de Sum ), la expresión se evalúa como constexpr . Por lo tanto, la llamada a Sum(-10, 20) será una expresión constante en tiempo de compilación que resultará en 30 .

Estática si declaración

C ++ 17

La if constexpr se puede usar para compilar condicionalmente el código. La condición debe ser una expresión constante. La rama no seleccionada se descarta. No se crea una instancia de una declaración descartada dentro de una plantilla. Por ejemplo:

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
}

Además, no es necesario definir las variables y funciones que solo se usan dentro de las sentencias descartadas, y las sentencias de return descartadas no se utilizan para la deducción del tipo de devolución de la función.

if constexpr es distinto de #ifdef . #ifdef compila condicionalmente el código, pero solo en función de las condiciones que se pueden evaluar en el momento del preprocesamiento. Por ejemplo, #ifdef no podría usarse para compilar condicionalmente el código dependiendo del valor de un parámetro de plantilla. Por otro lado, if constexpr no puede usarse para descartar un código sintácticamente no válido, mientras que #ifdef puede.

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow