¿Qué determina si una función constexpr es una expresión constante?
(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 increment
no 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 f
realidad es un constexpr.
Si alguien puede dar algunas ideas sobre constexpr en c ++ y esta aparente inconsistencia, lo agradecería enormemente.
Respuestas
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 f
se 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.
Dado que no está llamando f
a una expresión constante, su pregunta es si el compilador debe diagnosticar que f
no se puede llamar en una expresión constante, basándose únicamente en su definición .
Los requisitos para la definición de una constexpr
funció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 f
no 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 , constexpr
funciones como f
esa 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.
En realidad, no está "llamando" f
en 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
El estándar requiere que una constexpr
funció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 constexpr
funció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.