O que determina se uma função constexpr é uma expressão constante?
(o compilador usado é gcc com c ++ 17 até onde eu sei (difícil de encontrar no Visual Studio))
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
increment( v );
return v;
}
int main( )
{
cout << f( ) << '\n';
}
O código acima fornece o erro na compilação:
a função constexpr 'f' não pode resultar em uma expressão constante.
Pelo que entendi, isso ocorre porque a função increment
não é um constexpr. O que me confunde é que o seguinte código é compilado corretamente:
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
for( int i = 0; i < 1; ++i )
{
increment( v );
}
return v;
}
int main( )
{
cout << f( ) << '\n';
}
Esse código é funcionalmente o mesmo e é compilado, embora increment ainda não seja um constexpr. Não entendo como é possível que um loop for no intervalo [0, 1) faça com que o compilador perceba que a função f
na verdade é um constexpr.
Se alguém puder dar algumas dicas sobre constexpr em c ++ e essa aparente inconsistência, eu agradeço muito.
Respostas
Ambos os programas são "malformados, sem necessidade de diagnóstico", de acordo com [dcl.constexpr] / 6 :
Para uma função constexpr ou construtor constexpr que não é padronizado nem um modelo, se nenhum valor de argumento existir de forma que uma invocação da função ou construtor possa ser uma subexpressão avaliada de uma expressão constante central, ou, para um construtor, uma subexpressão avaliada de a expressão completa de inicialização de algum objeto inicializado com constante ( [basic.start.static] ), o programa está mal formado, nenhum diagnóstico necessário.
É um pouco estranho que o gcc simplesmente não perceba o problema com o segundo programa, mas ainda está em conformidade.
Observe que um diagnóstico seria necessário se f
fosse usado em um contexto que realmente requer uma expressão constante, por exemplo constexpr int n = f();
.
Algumas coisas nunca são permitidas em uma função constexpr. Eles exigem um diagnóstico (normalmente uma mensagem de erro), mesmo que a função nunca seja usada em uma expressão constante - veja a resposta de cigien . Mas os programas em questão não violam nenhuma dessas regras mais rígidas.
Já que você não está chamando f
uma expressão constante, sua pergunta está perguntando se o compilador é necessário para diagnosticar que f
não pode ser chamado em uma expressão constante, com base apenas em sua definição .
Os requisitos para a definição de uma constexpr
função são enumerados aqui :
A definição de uma função constexpr deve satisfazer os seguintes requisitos:
(3.1) seu tipo de retorno (se houver) deve ser um tipo literal;
(3.2) cada um de seus tipos de parâmetro deve ser um tipo literal;
(3.3) não deve ser uma co-rotina;
(3.4) se a função é um construtor ou destruidor, sua classe não deve ter nenhuma classe base virtual;
(3.5) seu corpo de funções não deve incluir
(3.5.1) uma instrução goto,
(3.5.2) uma etiqueta identificadora,
(3.5.3) uma definição de uma variável de tipo não literal ou de duração de armazenamento estático ou thread.
Como pode ser visto, a definição de f
não viola nenhum dos requisitos da lista. Portanto, um compilador está em conformidade se optar por não diagnosticar isso.
Como apontado na resposta do aschepler , constexpr
funções como f
essa não podem ser chamadas em uma expressão constante, mas não são diagnosticáveis como tal, são consideradas mal formadas-sem-diagnóstico-necessário.
Você não está realmente "chamando" f
em tempo de compilação.
se sua função principal incluísse: static_assert(f() == 1, "f() returned 1");
Suspeito que você obteria um erro "f () não é uma expressão constante".
Aqui está uma questão relacionada
O padrão requer que uma constexpr
função seja realmente avaliada em tempo de compilação para algum conjunto de parâmetros, mas não todos. Não requer compiladores para diagnosticar uma constexpr
função fazendo certas coisas que podem não ser em tempo de compilação em algumas circunstâncias, ou mesmo se tal função possui tal conjunto de parâmetros. Isso evita que eles tenham que resolver o problema da parada.