Considere este programa:
#include <cstdint>
using my_time_t = uintptr_t;
int main() {
const my_time_t t = my_time_t(nullptr);
}
No se pudo compilar con msvc v19.24:
<source>(5): error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'my_time_t'
<source>(5): note: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type
<source>(5): error C2789: 't': an object of const-qualified type must be initialized
<source>(5): note: see declaration of 't'
Compiler returned: 2
pero clang (9.0.1) y gcc (9.2.1) "comen" este código sin ningún error.
Me gusta el comportamiento de MSVC, pero ¿está confirmado por estándar? En otras palabras, ¿es un error en clang / gcc o es posible interpretar estándar que este es el comportamiento correcto de gcc / clang?
Respuestas:
En mi opinión, MSVC no se comporta conforme a la norma.
Estoy basando esta respuesta en C ++ 17 (borrador N4659), pero C ++ 14 y C ++ 11 tienen una redacción equivalente.
my_time_t(nullptr)
es una expresión postfix y debido a quemy_time_t
es un tipo y(nullptr)
es una expresión única en una lista de inicializadores entre paréntesis, es exactamente equivalente a una expresión de conversión explícita. ( [expr.type.conv] / 2 )El reparto explícito intenta con algunos cambios específicos de C ++ (con extensiones), en particular también
reinterpret_cast
. ( [expr.cast] /4.4 ) Los lanzamientos que se probaron anteriormentereinterpret_cast
sonconst_cast
ystatic_cast
(con extensiones y también en combinación), pero ninguno de estos puede lanzarsestd::nullptr_t
a un tipo integral.Pero
reinterpret_cast<my_time_t>(nullptr)
debería tener éxito porque [expr.reinterpret.cast] / 4 dice que un valor de tipostd::nullptr_t
se puede convertir a un tipo integral como si fuera byreinterpret_cast<my_time_t>((void*)0)
, lo cual es posible porquemy_time_t = std::uintptr_t
debería ser un tipo lo suficientemente grande como para representar todos los valores de puntero y bajo esta condición el El mismo párrafo estándar permite la conversiónvoid*
a un tipo integral.Es particularmente extraño que MSVC permita la conversión si se utiliza la notación de conversión en lugar de la notación funcional:
fuente
static_cast
en cuenta que, en particular, algunos casos tienen la intención de atrapar la escalera de lanzamiento de estilo C (por ejemplo, un lanzamiento de estilo C a una base ambigua es un mal formado enstatic_cast
lugar de unreinterpret_cast
), pero ninguno se aplica aquí.my_time_t(nullptr)
es, por definición, el mismo que(my_time_t)nullptr
, por lo que MSVC ciertamente está equivocado al aceptar uno y rechazar el otro.Aunque no puedo encontrar ninguna mención explícita en este Borrador de Trabajo Estándar C ++ (de 2014) que la conversión de
std::nullptr_t
a un tipo integral esté prohibida, ¡tampoco se menciona que tal conversión esté permitida!Sin embargo, el caso de conversión de
std::nullptr_t
abool
se menciona explícitamente:Además, el único lugar en este documento borrador donde se menciona la conversión de
std::nullptr_t
a un tipo integral, es en la sección "reinterpret_cast":Entonces, a partir de estas dos observaciones, uno podría (IMHO) suponer razonablemente que el
MSVC
compilador es correcto.EDITAR : Sin embargo, su uso del "reparto de notación funcional" en realidad puede sugerir lo contrario. El
MSVC
compilador no tiene ningún problema al usar una conversión de estilo C, por ejemplo:pero (como en su código), se queja de esto:
Sin embargo, del mismo Proyecto de Norma:
La "expresión de conversión correspondiente (5.4)" puede referirse a una conversión de estilo C.
fuente
Todos son estándar (ref. Draft n4659 para C ++).
nullptr
se define en [lex.nullptr] como:Incluso si las notas no son normativas, esta deja en claro que para el estándar,
nullptr
se espera que se convierta en un valor de puntero nulo .Más tarde encontramos en [conv.ptr]:
Una vez más, lo que requiere el estándar es que
0
se puede convertir a aystd::nullptr_t
quenullptr
se puede convertir a cualquier tipo de puntero.Mi lectura es que el estándar no tiene ningún requisito sobre si
nullptr
se puede convertir directamente a un tipo integral o no. Desde ese punto en adelante:void *
estuviera involucrada una conversión intermedia .fuente
nullptr
no porque tenga el tipo no integralstd::nullptr_t
. 0 se puede convertir a unstd::nullptr_t
valor, pero no al literalnullptr
. Todo esto es intencional,std::nullptr_t
es un tipo más restringido para evitar conversiones no deseadas.