constexpr e consteval
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 87 de 104.
O C++ permite que computações ocorram em tempo de compilação em vez de tempo de execução, o que pode melhorar significativamente o desempenho. A palavra-chave constexpr (C++11) e a palavra-chave consteval (C++20) oferecem controle sobre quando as expressões são avaliadas.
Uma função constexpr pode ser avaliada em tempo de compilação se receber argumentos constantes, mas também pode ser executada em tempo de execução com entradas não constantes:
#include <iostream>
constexpr int square(int n) {
return n * n;
}
int main() {
constexpr int compileTime = square(5); // Avaliado em tempo de compilação
int x = 7;
int runtime = square(x); // Avaliado em tempo de execução
std::cout << compileTime << "\n"; // 25
std::cout << runtime << "\n"; // 49
}Quando você precisar garantir a avaliação em tempo de compilação, use consteval. Uma função consteval deve produzir uma constante - chamá-la com valores de tempo de execução é um erro de compilação:
consteval int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int result = factorial(5); // OK: 120 computado em tempo de compilação
// int x = 5;
// int bad = factorial(x); // ERRO: x não é uma constante
}Você também pode usar constexpr com variáveis e até mesmo construtores de classe, permitindo que objetos inteiros sejam criados em tempo de compilação. Isso é particularmente útil para tabelas de consulta, valores de configuração ou quaisquer dados que não mudam durante a execução do programa.
Desafio
FácilVocê organizará seu código em três arquivos:
MathUtils.h: Defina suas funções matemáticas de tempo de compilação.Crie as seguintes funções:
cube— uma funçãoconstexprque recebe uminte retorna seu cubo (n * n * n)triangularNumber— uma funçãoconstexprque calcula o enésimo número triangular usando a fórmula n * (n + 1) / 2. Isso deve ser sempre avaliado em tempo de compilação.sumOfSquares— uma funçãoconstexprque recebe dois inteiros e retorna a soma de seus quadrados (a*a + b*b)
Config.h: Crie uma estrutura de configuração usando constantes de tempo de compilação.Defina uma struct
Configcom um construtorconstexprque recebe três inteiros:width,height, edepth. Armazene-os como membros públicos. Adicione também um métodoconstexprchamadovolume()que retorna width * height * depth.Abaixo da struct, crie uma constante global
constexprchamadaDEFAULT_CONFIGinicializada com os valores 10, 20 e 5.main.cpp: Leia dois inteiros da entrada representando valores de tempo de execução.Primeiro, demonstre a avaliação em tempo de compilação criando variáveis
constexpr:- Armazene
cube(4)em uma variável constexpr e imprima:Cube of 4: [value] - Armazene
triangularNumber(10)em uma variável constexpr e imprima:10th triangular number: [value] - Imprima o volume da configuração padrão:
Default volume: [value]
Depois, demonstre que as funções
constexprtambém podem funcionar em tempo de execução usando seus dois valores de entrada:- Chame
cube()com a primeira entrada e imprima:Cube of [input]: [result] - Chame
sumOfSquares()com ambas as entradas e imprima:Sum of squares: [result]
- Armazene
Por exemplo, com as entradas 3 e 4:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 3: 27
Sum of squares: 25Com as entradas 5 e 12:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 5: 125
Sum of squares: 169Folha de consulta
O C++ permite computações em tempo de compilação usando as palavras-chave constexpr (C++11) e consteval (C++20).
Uma função constexpr pode ser avaliada em tempo de compilação com argumentos constantes, mas também é executada em tempo de execução com entradas não constantes:
constexpr int square(int n) {
return n * n;
}
constexpr int compileTime = square(5); // Evaluated at compile time
int x = 7;
int runtime = square(x); // Evaluated at runtimeUma função consteval deve ser avaliada em tempo de compilação. Chamá-la com valores de tempo de execução causa um erro de compilação:
consteval int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int result = factorial(5); // OK: computed at compile time
// int x = 5;
// int bad = factorial(x); // ERROR: x is not a constantVocê pode usar constexpr com variáveis, construtores de classe e métodos para permitir a criação de objetos em tempo de compilação:
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); // Created at compile timeExperimente você mesmo
#include <iostream>
#include "MathUtils.h"
#include "Config.h"
using namespace std;
int main() {
// Lê dois inteiros da entrada
int input1, input2;
cin >> input1;
cin >> input2;
// TODO: Demonstrar avaliação em tempo de compilação
// Crie uma variável constexpr armazenando cube(4) e imprima: "Cube of 4: [value]"
// TODO: Crie uma variável constexpr armazenando triangularNumber(10)
// e imprima: "10th triangular number: [value]"
// TODO: Imprima o volume da configuração padrão: "Default volume: [value]"
// TODO: Demonstrar o uso em tempo de execução de funções constexpr
// Chame cube() com input1 e imprima: "Cube of [input1]: [result]"
// TODO: Chame sumOfSquares() com ambas as entradas
// e imprima: "Sum of squares: [result]"
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários2Gerenciamento de Memória
Memória Stack vs HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico12Recursos do C++ Moderno
Semântica de Movimento e RvaluesPerfect ForwardingExpressões Lambda em Detalhesstd::function & std::bindconstexpr e constevalStructured Bindingsoptional, variant, any