Usando gráficos de punteros en funciones constexpr
Estoy tratando de entender las reglas de constexpr en relación a los punteros, contenidos dentro de estructuras de datos constexpr, que hacen referencia a otras partes de esa estructura de datos constexpr.
Considere el siguiente fragmento de código:
struct selfref{
selfref *next{nullptr};
};
struct exec{
selfref mem[1] = {selfref{}};
constexpr exec(){
mem[0].next = &mem[0];
}
};
Si intento instanciar exec
como una variable local constexpr, puedo seguir los punteros desde ella:
int main(){
static constexpr exec ret;
static constexpr selfref *ptr = ret.mem[0].next;
}
Pero si en cambio la retorno desde una función trivial y no cambio nada más:
constexpr exec do_thing(){
return exec{};
}
int main(){
static constexpr auto ret = do_thing();
static constexpr selfref *ptr = ret.mem[0].next;
}
De repente obtengo un error con g++-11 y g++-12:
minimal-interesting-example2.cpp: In function ‘int main()’:
minimal-interesting-example2.cpp:18:42: error: ‘exec{selfref [1]{selfref{(<anonymous>.exec::mem[0])}}}’ is not a constant expression
18 | static constexpr auto ret = do_thing();
|
Pero no con clang++-13. Demo: link
¿Cuál compilador tiene razón? Y sin importar esa respuesta, ¿qué está sucediendo aquí?
EDIT: Parece que GCC al menos cree que este código debería funcionar según este reporte de error: link. Aun así, me interesaría mucho saber si hay alguien que conozca el estándar y pueda confirmar que esto debería funcionar.
davy.ai
La pregunta gira en torno al comportamiento del especificador
constexpr
y su interacción con punteros contenidos dentro de estructuras de datosconstexpr
que hacen referencia a otras partes de esa misma estructura de datos. El fragmento específico de código incluye una estructuraselfref
que contiene un miembronext
, el cual es un puntero a una estructuraselfref
. Otra estructuraexec
contiene un arreglomem
de estructurasselfref
y define un constructorconstexpr
que inicializamem[0].next
para que apunte al primer elemento demem
. El comportamiento de este código se prueba instanciandoexec
como una constanteconstexpr
local enmain
y siguiendo los punteros desde ella, y posteriormente devolviéndola desde una función trivial y siguiendo los punteros desde ella.El problema con el segundo caso de prueba es que, aunque se compila correctamente con clang++-13, produce un error con g++-11 y g++-12. El mensaje de error indica que
exec { selfref [1] { selfref { (&<anonymous>.exec::mem[0]) }}}
no es una expresión constante. Esto significa que g++-11 y g++-12 no pueden evaluar la llamadaconstexpr
ado_thing()
en tiempo de compilación, a pesar de que devuelve un objetoconstexpr
. Este comportamiento contradice lo que sugiere el informe de error de GCC mencionado en la edición.Es difícil decir definitivamente cuál compilador es correcto sin referirse al estándar C++20. Sin embargo, el hecho de que el código se comporte de manera diferente entre los dos compiladores sugiere que puede haber cierta ambigüedad o falta de claridad en el estándar con respecto a este escenario específico. Es posible que clang++-13 esté implementando una interpretación más permisiva del estándar mientras que g++-11 y g++-12 se adhieren más estrictamente a él.
En general, el especificador
constexpr
indica al compilador que una expresión puede ser evaluada en tiempo de compilación y, por lo tanto, se puede utilizar en contextos que requieren expresiones constantes. Sin embargo, el comportamiento deconstexpr
en escenarios más complejos que involucran punteros y estructuras de datos puede ser difícil de entender. En este caso, parece que el uso de punteros dentro de estructuras de datosconstexpr
está causando dificultades a los compiladores para evaluar las expresiones involucradas.