O que determina se uma função constexpr é uma expressão constante?

Nov 30 2020

(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 incrementnã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 fna verdade é um constexpr.

Se alguém puder dar algumas dicas sobre constexpr em c ++ e essa aparente inconsistência, eu agradeço muito.

Respostas

12 aschepler Nov 30 2020 at 22:23

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 ffosse 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.

6 cigien Nov 30 2020 at 22:27

Já que você não está chamando fuma expressão constante, sua pergunta está perguntando se o compilador é necessário para diagnosticar que fnão pode ser chamado em uma expressão constante, com base apenas em sua definição .

Os requisitos para a definição de uma constexprfunçã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 fnã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 , constexprfunções como fessa 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.

2 MarshallClow Nov 30 2020 at 22:22

Você não está realmente "chamando" fem 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

Sneftel Nov 30 2020 at 22:12

O padrão requer que uma constexprfunçã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 constexprfunçã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.