El extraño problema del lenguaje es CWG 1581 :
La cláusula 15 [especial] es perfectamente clara de que las funciones miembro especiales solo se definen implícitamente cuando se usan odr. Esto crea un problema para las expresiones constantes en contextos no evaluados:
struct duration {
constexpr duration() {}
constexpr operator int() const { return 0; }
};
// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});
El problema aquí es que no se nos permite definir implícitamente constexpr duration::duration(duration&&)
en este programa, por lo que la expresión en la lista de inicializadores no es una expresión constante (porque invoca una función constexpr que no se ha definido), por lo que el inicializador entre paréntesis contiene una conversión de reducción , por lo que el programa está mal formado.
Si descomentamos la línea # 1, el constructor de movimiento está implícitamente definido y el programa es válido. Esta acción espeluznante a distancia es extremadamente desafortunada. Las implementaciones divergen en este punto.
Puede leer el resto de la descripción del problema.
Se adoptó una resolución para este problema en P0859 en Albuquerque en 2017 (después del envío de C ++ 17). Ese problema fue un bloqueador para poder tener un constexpr std::swap
(resuelto en P0879 ) y un constexpr std::invoke
(resuelto en P1065 , que también tiene ejemplos CWG1581), ambos para C ++ 20.
El ejemplo más simple de entender aquí, en mi opinión, es el código del informe de error LLVM señalado en P1065:
template<typename T>
int f(T x)
{
return x.get();
}
template<typename T>
constexpr int g(T x)
{
return x.get();
}
int main() {
// O.K. The body of `f' is not required.
decltype(f(0)) a;
// Seems to instantiate the body of `g'
// and results in an error.
decltype(g(0)) b;
return 0;
}
CWG1581 se trata de cuándo se definen las funciones de miembro constexpr, y la resolución garantiza que solo se definan cuando se usan. Después de P0859, lo anterior está bien formado (el tipo de b
es int
).
Dado std::swap
y std::invoke
ambos tienen que confiar en la comprobación de las funciones miembro (mover la construcción / asignación en el primero y el operador de llamada / llamadas sustitutas en el segundo), ambos dependían de la resolución de este problema.