Considere el siguiente ejemplo ( fragmento (0) ):
struct X
{
constexpr int get() const { return 0; }
};
void foo(const X& x)
{
constexpr int i = x.get();
}
int main()
{
foo(X{});
}
El ejemplo anterior se compila con todas las versiones g++
anteriores g++ 10.x
y nunca compiladas bajo clang++
. El mensaje de error es:
error: 'x' is not a constant expression 8 | constexpr int i = x.get(); |
ejemplo en vivo en godbolt.org
Sin embargo, el tipo de error tiene sentido, ya x
que nunca es una expresión constante en el cuerpo de foo
:
X::get()
está marcadoconstexpr
y no depende del estado dex
;Cambiar
const X&
aconst X
hace que el código se compile con cada fragmento del compilador (en godbolt.org) (1) .
Se vuelve aún más interesante cuando marco X::get()
como static
( (en godbolt.org) fragmento (2) ). Con ese cambio, todas las versiones probadas de g++
compilación (incluida la troncal), aunque clang++
siempre fallan al compilar.
Entonces, mis preguntas:
¿Es
g++ 9.x
correcto aceptar el fragmento (0) ?¿Todos los compiladores son correctos al aceptar el fragmento (1) ? Si es así, ¿por qué es importante la referencia?
Son
g++ 9.x
yg++ trunk
correcta al aceptar fragmento (2) ?
x
enfoo
no es una expresión constante. Incluso hay un informe de error antiguo (rechazado informalmente) en el sonido metálico por su comportamiento correcto (mientras que GCC tenía un error real).Respuestas:
No.
Sí lo son.
Una expresión constante no puede usar una expresión id nombrando una referencia que no tenga una inicialización de expresión constante previa o que comience su vida útil durante la evaluación de la expresión constante. [expr.const] /2.11 ( igual en C ++ 20 )
Lo mismo no es cierto si está nombrando una variable sin referencia sin involucrar ninguna conversión de valor a valor.
x.get()
solo se refiere ax
lvalue y solo llama a unaconstexpr
función que en realidad no accede a ningún miembrox
, por lo que no hay problema.No, porque la expresión todavía contiene la subexpresión
x
que viola la regla mencionada anteriormente.fuente