Parece-me que ter uma "função que sempre retorna 5" está quebrando ou diluindo o significado de "chamar uma função". Deve haver um motivo ou uma necessidade desse recurso ou não seria no C ++ 11. Por que está aí?
// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
Parece-me que, se eu escrevesse uma função que retornasse um valor literal e fiz uma revisão de código, alguém me diria que eu deveria declarar um valor constante em vez de escrever o retorno 5.
constexpr
? Nesse caso, posso ver um uso.const
. De fato, a intenção obrigatória é útil ! As dimensões da matriz são o exemplo canônico.Respostas:
Suponha que faça algo um pouco mais complicado.
Agora você tem algo que pode ser avaliado em constante, mantendo boa legibilidade e permitindo um processamento um pouco mais complexo do que apenas definir uma constante para um número.
Basicamente, fornece uma boa ajuda para a manutenção, pois fica mais óbvio o que você está fazendo. Tomemos
max( a, b )
por exemplo:É uma escolha bastante simples, mas significa que, se você chamar
max
com valores constantes, é explicitamente calculado em tempo de compilação e não em tempo de execução.Outro bom exemplo seria uma
DegreesToRadians
função. Todo mundo acha graus mais fáceis de ler do que radianos. Embora você saiba que 180 graus está em radianos, é muito mais claro escrito da seguinte maneira:Muitas informações boas aqui:
http://en.cppreference.com/w/cpp/language/constexpr
fonte
Introdução
constexpr
não foi introduzido como uma maneira de dizer à implementação que algo pode ser avaliado em um contexto que requer uma expressão constante ; implementações em conformidade conseguiu provar isso antes do C ++ 11.Algo que uma implementação não pode provar é a intenção de um determinado pedaço de código:
O que seria do mundo sem
constexpr
?Digamos que você esteja desenvolvendo uma biblioteca e compreenda que deseja calcular a soma de cada número inteiro no intervalo
(0,N]
.A falta de intenção
Um compilador pode facilmente provar que a função acima pode ser chamada em uma expressão constante se o argumento passado for conhecido durante a tradução; mas você não declarou isso como uma intenção - aconteceu de acontecer.
Agora alguém aparece, lê sua função, faz a mesma análise que o compilador; " Oh, esta função é utilizável em uma expressão constante!" e escreve o seguinte trecho de código.
A otimização
Você, como um desenvolvedor de biblioteca "impressionante" , decide que
f
deve armazenar em cache o resultado ao ser chamado; quem gostaria de calcular o mesmo conjunto de valores repetidamente?O resultado
Ao introduzir sua otimização boba, você acabou de quebrar todos os usos de sua função que estavam em um contexto em que uma expressão constante era necessária.
Você nunca prometeu que a função era utilizável em uma expressão constante e , sem ela,
constexpr
não haveria maneira de oferecer tal promessa.Então, por que precisamos
constexpr
?O uso principal do constexpr é declarar intenção .
Se uma entidade não estiver marcada como
constexpr
- ela nunca foi projetada para ser usada em uma expressão constante ; e, mesmo que seja, contamos com o compilador para diagnosticar esse contexto (porque desconsidera nossa intenção).fonte
constexpr
expressões. Em outras palavras, praticamente qualquer coisa pode ser anotadaconstexpr
(talvez um dia ela simplesmente desapareça por causa disso?), E a menos que se tenha um critério de quando usarconstexpr
ou não, praticamente todo o código será escrito como tal .I/O
,syscall
edynamic memory allocation
definitivamente cann't ser marcado comoconstexpr
Além disso, nem tudo deve serconstexpr
.constexpr
é uma garantia de algum tipo de comportamento. Assim comoconst
faz.int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)];
não consiga compilar em nenhum lugar?Tome
std::numeric_limits<T>::max()
: por qualquer motivo, este é um método.constexpr
seria benéfico aqui.Outro exemplo: você deseja declarar uma matriz C (ou a
std::array
) que é tão grande quanto outra matriz. A maneira de fazer isso no momento é assim:Mas não seria melhor ser capaz de escrever:
Graças a
constexpr
, você pode:fonte
constexpr
força o complier a fazer com que a função retorne um valor em tempo de compilação (se possível).constexpr
ele, não pode ser usado em uma declaração de tamanho de matriz nem como argumento de modelo, independentemente de o resultado da chamada de função ser uma constante em tempo de compilação ou não. Esses dois são basicamente os únicos casos de uso,constexpr
mas pelo menos o argumento de modelo é importante.-pedantic
opção e ela será sinalizada como um erro.constexpr
funções são realmente legais e um ótimo complemento para o c ++. No entanto, você está certo de que a maioria dos problemas que resolve pode ser deselegantemente contornada com macros.No entanto, um dos usos de
constexpr
não possui constantes digitadas equivalentes a C ++ 03.fonte
four
não resolve. Eu realmente tive que cavar para descobrir quem estava pegando o endereço da minhastatic const
variável.four
nemfive
estão no escopo.enum class
tipo, ele corrige alguns problemas de enumeração.Pelo que li, a necessidade de constexpr vem de um problema na metaprogramação. Classes de características podem ter constantes representadas como funções, pense: numeric_limits :: max (). Com constexpr, esses tipos de funções podem ser usados na metaprogramação ou como limites de array, etc.
Outro exemplo de primeira linha seria que, para interfaces de classe, você pode querer que tipos derivados definam suas próprias constantes para alguma operação.
Editar:
Depois de bisbilhotar o SO, parece que outros criaram alguns exemplos do que pode ser possível com o constexprs.
fonte
constexpr
é mais especificamente útil em um compilador com um poderoso sistema de avaliação de expressões em tempo de compilação. C ++ realmente não tem pares nesse domínio. (que é um forte aplauso para C ++ 11, IMHO)Do discurso de Stroustrup em "Going Native 2012":
fonte
Outro uso (ainda não mencionado) é
constexpr
construtores. Isso permite criar constantes de tempo de compilação que não precisam ser inicializadas durante o tempo de execução.Emparelhe isso com literais definidos pelo usuário e você terá suporte completo para classes literais definidas pelo usuário.
fonte
Costumava haver um padrão com metaprogramação:
Acredito que
constexpr
foi introduzido para permitir que você escreva essas construções sem a necessidade de modelos e construções estranhas com especialização, SFINAE e outras coisas - mas exatamente como você escreveria uma função de tempo de execução, mas com a garantia de que o resultado será determinado na compilação -Tempo.No entanto, observe que:
Compile isso
g++ -O3
e você verá quefact(10)
é realmente evado no tempo de compilação!Um compilador compatível com VLA (portanto, um compilador C no modo C99 ou um compilador C ++ com extensões C99) pode até permitir:
Mas, no momento,
constexpr
é C ++ fora do padrão - parece uma maneira de combater isso (mesmo sem o VLA, no caso acima). E ainda há o problema da necessidade de ter expressões constantes "formais" como argumentos de modelo.fonte
std::array<int, fact(2)>
e você verá que fato () não é avaliado em tempo de compilação. É apenas o otimizador do GCC fazendo um bom trabalho.Acabei de começar a mudar o projeto para o c ++ 11 e me deparei com uma situação perfeitamente boa para o constexpr, que limpa métodos alternativos de executar a mesma operação. O ponto principal aqui é que você só pode colocar a função na declaração de tamanho da matriz quando ela for declarada constexpr. Há várias situações em que vejo isso sendo muito útil para avançar com a área de código em que estou envolvido.
fonte
static inline constexpr const auto
provavelmente é melhor.Todas as outras respostas são ótimas, só quero dar um exemplo legal de uma coisa que você pode fazer com o constexpr que é incrível. O See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) é um analisador de HTML em tempo de compilação e um mecanismo de modelo. Isso significa que você pode inserir HTML e sair de uma árvore que possa ser manipulada. Fazer a análise em tempo de compilação pode oferecer um desempenho extra.
No exemplo da página do github:
fonte
Seu exemplo básico serve ao mesmo argumento que o das constantes. Por que usar
sobre
Porque é muito mais sustentável. O uso do constexpr é muito, muito mais rápido de escrever e ler do que as técnicas de metaprogramação existentes.
fonte
Pode ativar algumas novas otimizações.
const
tradicionalmente é uma dica para o sistema de tipos e não pode ser usado para otimização (por exemplo, umaconst
função membro podeconst_cast
e modifica o objeto de qualquer maneira, legalmente, portantoconst
não pode ser confiável para otimização).constexpr
significa que a expressão é realmente constante, desde que as entradas para a função sejam const. Considerar:Se isso for exposto em algum outro módulo, o compilador não pode confiar que
GetNumber()
não retornará valores diferentes cada vez que for chamado - mesmo consecutivamente sem chamadas não-const no meio - porqueconst
poderia ter sido descartado na implementação. (Obviamente, qualquer programador que fez isso deve ser filmado, mas a linguagem permite, portanto, o compilador deve cumprir as regras.)Adicionando
constexpr
:O compilador agora pode aplicar uma otimização na qual o valor de retorno
GetNumber()
é armazenado em cache e eliminar chamadas adicionais paraGetNumber()
, porqueconstexpr
é uma garantia mais forte de que o valor de retorno não será alterado.fonte
const
pode ser usado na otimização ... É um comportamento indefinido modificar um valor definido const mesmo depois de umconst_cast
IIRC. Eu esperava que fosse consistente para asconst
funções de membro, mas precisaria verificar isso com o padrão. Isso significa que o compilador pode fazer otimizações com segurança lá.int x
vs.const int x
), é seguro modificá-loconst_cast
afastando const em um ponteiro / referência a ele. Caso contrário,const_cast
sempre chamaria um comportamento indefinido e seria inútil :) Nesse caso, o compilador não possui informações sobre a consistência do objeto original, portanto não pode dizer.int GetNumber() const = 0;
) deve declarar oGetNumber()
método virtual. O segundo (constexpr int GetNumber() const = 0;
) não é válido porque o especificador puro (= 0
) implica que o método seja virtual, mas os constexpr não devem ser virtuais (ref: en.cppreference.com/w/cpp/language/constexpr )Quando usar
constexpr
:fonte
constexpr
deve ser preferida às macros do pré-processador ouconst
.É útil para algo como
Amarre isso com uma classe de características ou algo semelhante e isso se torna bastante útil.
fonte