Invocación de la función miembro `constexpr` mediante referencia - clang vs gcc

8

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.xy 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 xque nunca es una expresión constante en el cuerpo de foo:

  • X::get()está marcado constexpry no depende del estado de x;

  • Cambiar const X&a const Xhace 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.xcorrecto 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.xy g++ trunkcorrecta al aceptar fragmento (2) ?

Vittorio Romeo
fuente
constexpr es una 'máscara' a un valor y una constante es una variable que usted informa que no se cambiará. Los valores no pueden ser referenciados o puntiagudos pero const sí. El miembro de la función get () se procesa en tiempo de compilación. En otras palabras, es 0 para todos los casos. Como dije antes, los valores no pueden ser referenciados o apuntados
TheArquitect
3
Creo que [expr.const] /2.11 se aplica aquí, y xen foono 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).
dfri
3
Escribió sobre esto recientemente, parece relevante: brevzin.github.io/c++/2020/02/05/constexpr-array-size
Barry
@Barry nice, en su artículo también tiene la referencia al informe de error de GCC correspondiente (podría encontrar el clang rechazado) que permitió que gcc 9.x aceptara el fragmento 0, que desde entonces se ha corregido.
dfri

Respuestas:

12

¿Es g ++ 9.x correcto al aceptar el fragmento (0)?

No.

¿Todos los compiladores son correctos al aceptar el fragmento (1)? Si es así, ¿por qué es importante la referencia?

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 a xlvalue y solo llama a una constexprfunción que en realidad no accede a ningún miembro x, por lo que no hay problema.

¿Son correctos g ++ 9.xy g ++ trunk al aceptar el fragmento (2)?

No, porque la expresión todavía contiene la subexpresión xque viola la regla mencionada anteriormente.

nuez
fuente
Informe de error relevante (rechazado informalmente) sobre clang señalando la misma corrección en clang (siempre) no compilando el fragmento 0.
dfri