Considérez ce programme:
#include <cstdint>
using my_time_t = uintptr_t;
int main() {
const my_time_t t = my_time_t(nullptr);
}
Il n'a pas réussi à compiler avec 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
mais clang (9.0.1) et gcc (9.2.1) "mangent" ce code sans aucune erreur.
J'aime le comportement MSVC, mais est-il confirmé par la norme? En d'autres termes, est-ce un bug dans clang / gcc ou il est possible d'interpréter la norme que c'est un bon comportement de gcc / clang?
Réponses:
À mon avis, MSVC ne se comporte pas conformément aux normes.
Je fonde cette réponse sur C ++ 17 (projet N4659), mais C ++ 14 et C ++ 11 ont un libellé équivalent.
my_time_t(nullptr)
est une expression de suffixe et, comme ilmy_time_t
s'agit d'un type et d'(nullptr)
une expression unique dans une liste d'initialisation entre parenthèses, elle est exactement équivalente à une expression de transtypage explicite. ( [expr.type.conv] / 2 )Le transtypage explicite essaie quelques conversions spécifiques C ++ différentes (avec extensions), en particulier également
reinterpret_cast
. ( [expr.cast] /4.4 ) Les transtypages essayés auparavantreinterpret_cast
sontconst_cast
etstatic_cast
(avec des extensions et également en combinaison), mais aucun d'entre eux ne peut être convertistd::nullptr_t
en un type intégral.Mais
reinterpret_cast<my_time_t>(nullptr)
devrait réussir parce que [expr.reinterpret.cast] / 4 dit qu'une valeur de typestd::nullptr_t
peut être convertie en un type intégral comme si parreinterpret_cast<my_time_t>((void*)0)
, ce qui est possible carmy_time_t = std::uintptr_t
devrait être un type suffisamment grand pour représenter toutes les valeurs du pointeur et dans cette condition, le le même paragraphe standard permet la conversion d'void*
un type intégral.Il est particulièrement étrange que MSVC autorise la conversion si la notation cast plutôt que la notation fonctionnelle est utilisée:
la source
static_cast
en particulier, certains cas visent à piéger l'échelle en fonte de style C (par exemple, une fonte de style C vers une base ambiguë est mal forméestatic_cast
plutôt qu'unreinterpret_cast
), mais aucun ne s'applique ici.my_time_t(nullptr)
est par définition le même que(my_time_t)nullptr
, donc MSVC a certainement tort d'accepter l'un et de rejeter l'autre.Bien que je ne puisse trouver aucune mention explicite dans ce Working Draft C ++ Standard (de 2014) que la conversion de
std::nullptr_t
vers un type intégral est interdite, il n'y a pas non plus mention qu'une telle conversion est autorisée!Cependant, le cas de la conversion de
std::nullptr_t
versbool
est explicitement mentionné:De plus, le seul endroit dans ce projet de document où la conversion de
std::nullptr_t
à un type intégral est mentionné, est dans la section "reinterpret_cast":Ainsi, à partir de ces deux observations, on pourrait (à mon humble avis) raisonnablement supposer que le
MSVC
compilateur est correct.EDIT : Cependant, votre utilisation de la «fonte de notation fonctionnelle» peut en fait suggérer le contraire! Le
MSVC
compilateur n'a aucun problème à utiliser un cast de style C, par exemple:mais (comme dans votre code), il s'en plaint:
Pourtant, à partir du même projet de norme:
L '"expression de conversion correspondante (5.4)" peut faire référence à une conversion de style C.
la source
Tous sont conformes à la norme (réf. Projet n4659 pour C ++).
nullptr
est défini dans [lex.nullptr] comme:Même si les notes ne sont pas normatives, celle-ci indique clairement que pour la norme, elle
nullptr
devrait être convertie en une valeur de pointeur nulle .On retrouve plus tard dans [conv.ptr]:
Là encore, ce qui est requis par la norme, c'est qu'il
0
peut être converti en astd::nullptr_t
et quinullptr
peut être converti en n'importe quel type de pointeur.Ma lecture est que la norme n'a aucune exigence quant à savoir si elle
nullptr
peut être convertie directement en type intégral ou non. À partir de là:void *
conversion intermédiaire était impliquée.la source
nullptr
n'est pas le cas car elles ont le type non intégralstd::nullptr_t
. 0 peut être converti enstd::nullptr_t
valeur, mais pas en littéralnullptr
. Tout cela est intentionnel,std::nullptr_t
est un type plus restreint pour empêcher les conversions involontaires.