constexpr y consteval
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 87 de 104.
C++ permite que los cálculos ocurran en tiempo de compilación en lugar de en tiempo de ejecución, lo que puede mejorar significativamente el rendimiento. La palabra clave constexpr (C++11) y la palabra clave consteval (C++20) le brindan control sobre cuándo se evalúan las expresiones.
Una función constexpr puede ser evaluada en tiempo de compilación si se le proporcionan argumentos constantes, pero también puede ejecutarse en tiempo de ejecución con entradas no constantes:
#include <iostream>
constexpr int square(int n) {
return n * n;
}
int main() {
constexpr int compileTime = square(5); // Evaluado en tiempo de compilación
int x = 7;
int runtime = square(x); // Evaluado en tiempo de ejecución
std::cout << compileTime << "\n"; // 25
std::cout << runtime << "\n"; // 49
}Cuando necesites garantizar la evaluación en tiempo de compilación, utiliza consteval. Una función consteval debe producir una constante; llamarla con valores en tiempo de ejecución es un error de compilación:
consteval int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int result = factorial(5); // OK: 120 calculado en tiempo de compilación
// int x = 5;
// int bad = factorial(x); // ERROR: x no es una constante
}También puedes usar constexpr con variables e incluso constructores de clase, permitiendo que se creen objetos enteros en tiempo de compilación. Esto es particularmente útil para tablas de búsqueda, valores de configuración o cualquier dato que no cambie durante la ejecución del programa.
Desafío
FácilOrganizarás tu código en tres archivos:
MathUtils.h: Define tus funciones matemáticas de tiempo de compilación.Crea las siguientes funciones:
cube— una funciónconstexprque toma uninty devuelve su cubo (n * n * n)triangularNumber— una funciónconstexprque calcula el n-ésimo número triangular usando la fórmula n * (n + 1) / 2. Esta debe evaluarse siempre en tiempo de compilación.sumOfSquares— una funciónconstexprque toma dos enteros y devuelve la suma de sus cuadrados (a*a + b*b)
Config.h: Crea una estructura de configuración utilizando constantes de tiempo de compilación.Define una estructura
Configcon un constructorconstexprque tome tres enteros:width,heightydepth. Almacénalos como miembros públicos. También añade un métodoconstexprllamadovolume()que devuelva width * height * depth.Debajo de la estructura, crea una constante global
constexprllamadaDEFAULT_CONFIGinicializada con los valores 10, 20 y 5.main.cpp: Lee dos enteros de la entrada que representen valores de tiempo de ejecución.Primero, demuestra la evaluación en tiempo de compilación creando variables
constexpr:- Almacena
cube(4)en una variable constexpr e imprime:Cube of 4: [value] - Almacena
triangularNumber(10)en una variable constexpr e imprime:10th triangular number: [value] - Imprime el volumen de la configuración por defecto:
Default volume: [value]
Luego, demuestra que las funciones
constexprtambién pueden funcionar en tiempo de ejecución utilizando tus dos valores de entrada:- Llama a
cube()con la primera entrada e imprime:Cube of [input]: [result] - Llama a
sumOfSquares()con ambas entradas e imprime:Sum of squares: [result]
- Almacena
Por ejemplo, con las entradas 3 y 4:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 3: 27
Sum of squares: 25Con las entradas 5 y 12:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 5: 125
Sum of squares: 169Hoja de referencia
C++ permite realizar cálculos en tiempo de compilación utilizando las palabras clave constexpr (C++11) y consteval (C++20).
Una función constexpr puede ser evaluada en tiempo de compilación con argumentos constantes, pero también se ejecuta en tiempo de ejecución con entradas que no son constantes:
constexpr int square(int n) {
return n * n;
}
constexpr int compileTime = square(5); // Evaluada en tiempo de compilación
int x = 7;
int runtime = square(x); // Evaluada en tiempo de ejecuciónUna función consteval debe ser evaluada en tiempo de compilación. Llamarla con valores de tiempo de ejecución provoca un error de compilación:
consteval int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int result = factorial(5); // OK: calculada en tiempo de compilación
// int x = 5;
// int bad = factorial(x); // ERROR: x no es una constantePuedes usar constexpr con variables, constructores de clase y métodos para permitir la creación de objetos en tiempo de compilación:
struct Config {
int width, height, depth;
constexpr Config(int w, int h, int d)
: width(w), height(h), depth(d) {}
constexpr int volume() const {
return width * height * depth;
}
};
constexpr Config config(10, 20, 5); // Creada en tiempo de compilaciónPruébalo tú mismo
#include <iostream>
#include "MathUtils.h"
#include "Config.h"
using namespace std;
int main() {
// Lee dos enteros de la entrada
int input1, input2;
cin >> input1;
cin >> input2;
// TODO: Demostrar la evaluación en tiempo de compilación
// Crea una variable constexpr que almacene cube(4) e imprime: "Cube of 4: [value]"
// TODO: Crea una variable constexpr que almacene triangularNumber(10)
// e imprime: "10th triangular number: [value]"
// TODO: Imprime el volumen de la configuración por defecto: "Default volume: [value]"
// TODO: Demostrar el uso en tiempo de ejecución de funciones constexpr
// Llama a cube() con input1 e imprime: "Cube of [input1]: [result]"
// TODO: Llama a sumOfSquares() con ambas entradas
// e imprime: "Sum of squares: [result]"
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genérico12Características de C++ moderno
Semántica de movimiento y RvaluesReenvío perfectoExpresiones Lambda en profundidadstd::function y std::bindconstexpr y constevalBindings estructuradosoptional, variant, any