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:
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:
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:
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
.
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";
De manera interesante y conveniente, también puede usar auto
:
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:
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
:
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:
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á:
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á:
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):
constexpr int Sum(int a, int b)
{
constexpr int a1 = a; // ERROR
..
Además, lo siguiente tampoco compilará:
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
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
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
}