guidelines - c++ best practices
AsignaciĆ³n compuesta en la funciĆ³n constexpr: gcc vs. clang (2)
Esto es simplemente un error de Clang. Si observamos la asignación compuesta [expr.ass]p7 , es equivalente a la asignación donde E1
se evalúa solo una vez:
El comportamiento de una expresión de la forma E1 op = E2 es equivalente a E1 = E1 op E2, excepto que E1 se evalúa solo una vez. En + = y - =, E1 tendrá un tipo aritmético o será un puntero a un tipo de objeto completamente definido, posiblemente calificado como CV. En todos los demás casos, E1 tendrá tipo aritmético.
Si observamos las restricciones en el requisito de la función de expresión constante en [dcl.constexpr]p3 , no tenemos ninguna restricción en la asignación:
La definición de una función constexpr deberá cumplir los siguientes requisitos:
- (3.1) su tipo de retorno será un tipo literal;
- (3.2) cada uno de sus tipos de parámetros será un tipo literal;
- (3.3) Su función-cuerpo no contendrá.
- (3.3.1) una definición asm,
- (3.3.2) una declaración de goto,
- (3.3.3) una etiqueta identificadora ([stmt.label]),
- (3.3.4) una definición de una variable de tipo no literal o de duración de almacenamiento de subprocesos o estática o para la que no se realiza ninguna inicialización.
[Nota: un cuerpo de función que es = eliminar o = predeterminado no contiene ninguno de los anteriores. - nota final]
y nada en [expr.const] agrega restricciones para este caso específico.
Me puse en contacto con Richard Smith fuera de línea y estuvo de acuerdo en que era un error y dijo:
Sí, es un error; ese código no está tomando en cuenta correctamente que el LHS podría necesitar una conversión a punto flotante antes del cálculo.
Informe de error de Clang archivado e informe de error de MSVC
template<class A, class B> constexpr int f(A a, B b) {
a /= b;
return a;
}
constexpr int x = f(2, 2); // a, b: int
constexpr int y = f(2., 2.); // a, b: double
constexpr int z = f(2, 2.); // a: int, b: double //<-- BOOM!
constexpr int w = f(2., 2); // a: double, b: int
int main() {}
El código no compilado en clang, produce el siguiente diagnóstico:
error: constexpr variable ''z'' must be initialized by a constant expression
MSVC se estrelló (de acuerdo con godbolt ) y gcc funciona bien. Si a /= b
simplemente se reemplaza por a = a / b
entonces todos lo aceptan. ¿Por qué?
Quien tiene razon Parece que está relacionado con la conversión implícita de reducción, pero entonces, ¿por qué a = a / b
funciona?
He enviado un parche para clang, que debería solucionar el error de clang.
Algunos detalles internos de Clang:
En clang, la evaluación de la expresión constante se maneja principalmente en lib/AST/ExprConstant.cpp . En particular, la asignación compuesta de enteros se maneja mediante CompoundAssignSubobjectHandler::found(APSInt &Value, QualType SubobjType)
. Antes de mi parche, esta función rechazaba incorrectamente cualquier RHS no entero:
if (!SubobjType->isIntegerType() || !RHS.isInt()) {
// We don''t support compound assignment on integer-cast-to-pointer
// values.
Info.FFDiag(E);
return false;
}
Mi parche soluciona esto al agregar una rama para el caso RHS.isFloat()
.
Tenga en cuenta que un problema similar no ocurre cuando LHS es un punto flotante y RHS es un número entero, aunque CompoundAssignSubobjectHandler
solo maneja el caso flotante <op> = float, porque el RHS siempre se promueve a un punto flotante en este caso.