¿Qué determina si una función constexpr es una expresión constante?

Nov 30 2020

(el compilador utilizado es gcc con c ++ 17 que yo sepa (es difícil encontrar esto en 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';
}

El código anterior da el error al compilar:

La función constexpr 'f' no puede resultar en una expresión constante.

Según tengo entendido, esto se debe a que la función incrementno es un constexpr. Lo que me confunde es que el siguiente código se compila bien:

#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';
}

Este código es funcionalmente el mismo y se compila, aunque el incremento todavía no es un constexpr. No entiendo cómo es posible que un bucle for a través del rango [0, 1) haga que el compilador se dé cuenta de que la función en frealidad es un constexpr.

Si alguien puede dar algunas ideas sobre constexpr en c ++ y esta aparente inconsistencia, lo agradecería enormemente.

Respuestas

12 aschepler Nov 30 2020 at 22:23

Ambos programas están "mal formados, no se requiere diagnóstico", según [dcl.constexpr] / 6 :

Para una función constexpr o constructor constexpr que no está predeterminado ni es una plantilla, si no existen valores de argumento tales que una invocación de la función o constructor podría ser una subexpresión evaluada de una expresión constante central o, para un constructor, una subexpresión evaluada de la expresión de inicialización completa de algún objeto inicializado constante ( [basic.start.static] ), el programa está mal formado, no se requiere diagnóstico.

Es un poco extraño que gcc simplemente no se dé cuenta del problema con el segundo programa, pero aún así se ajusta.

Tenga en cuenta que se necesitaría un diagnóstico si fse usara en un contexto que realmente requiera una expresión constante, por ejemplo constexpr int n = f();.

Algunas cosas nunca están permitidas en una función constexpr. Estos requieren un diagnóstico (generalmente un mensaje de error), incluso si la función nunca se usa en una expresión constante; consulte la respuesta de cigien . Pero los programas en la pregunta no violan ninguna de estas reglas más estrictas.

6 cigien Nov 30 2020 at 22:27

Dado que no está llamando fa una expresión constante, su pregunta es si el compilador debe diagnosticar que fno se puede llamar en una expresión constante, basándose únicamente en su definición .

Los requisitos para la definición de una constexprfunción se enumeran aquí :

La definición de una función constexpr deberá satisfacer los siguientes requisitos:

(3.1) su tipo de retorno (si lo hay) será un tipo literal;

(3.2) cada uno de sus tipos de parámetros será un tipo literal;

(3.3) no será una corrutina;

(3.4) si la función es un constructor o un destructor, su clase no tendrá ninguna clase base virtual;

(3.5) su cuerpo funcional no incluirá

(3.5.1) una declaración goto,

(3.5.2) una etiqueta de identificación,

(3.5.3) una definición de una variable de tipo no literal o de duración de almacenamiento estático o de subprocesos.

Como puede verse, la definición de fno viola ninguno de los requisitos de la lista. Por lo tanto, un compilador está conforme si elige no diagnosticar esto.

Como se señaló en la respuesta de Aschepler , constexprfunciones como fesa no se pueden llamar en una expresión constante, pero no se pueden diagnosticar como tales, se consideran mal formadas, sin necesidad de diagnóstico.

2 MarshallClow Nov 30 2020 at 22:22

En realidad, no está "llamando" fen tiempo de compilación.

si su función principal incluye: static_assert(f() == 1, "f() returned 1");Sospecho que obtendría un error "f () no es una expresión constante".

Aquí hay una pregunta relacionada

Sneftel Nov 30 2020 at 22:12

El estándar requiere que una constexprfunción sea realmente evaluable en tiempo de compilación para algún conjunto de parámetros, pero no para todos. No requiere que los compiladores diagnostiquen una constexprfunción que hace ciertas cosas que podrían no estar en tiempo de compilación en algunas circunstancias, o incluso si dicha función tiene tal conjunto de parámetros. Esto les evita tener que resolver el problema de la detención.