Betrachten Sie dieses Programm:
#include <cstdint>
using my_time_t = uintptr_t;
int main() {
const my_time_t t = my_time_t(nullptr);
}
Es konnte nicht mit msvc v19.24 kompiliert werden:
<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
aber clang (9.0.1) und gcc (9.2.1) "essen" diesen Code ohne Fehler.
Ich mag das MSVC-Verhalten, aber wird es standardmäßig bestätigt? Mit anderen Worten, ist es ein Fehler in clang / gcc oder ist es möglich, den Standard so zu interpretieren, dass dies das richtige Verhalten von gcc / clang ist?
Antworten:
Meiner Meinung nach verhält sich MSVC nicht standardkonform.
Ich stütze diese Antwort auf C ++ 17 (Entwurf N4659), aber C ++ 14 und C ++ 11 haben den gleichen Wortlaut.
my_time_t(nullptr)
ist ein Postfix-Ausdruck und damy_time_t
es sich um einen Typ und(nullptr)
einen einzelnen Ausdruck in einer Initialisierungsliste in Klammern handelt, entspricht er genau einem expliziten Cast-Ausdruck. ( [expr.type.conv] / 2 )Die explizite Besetzung versucht insbesondere auch einige verschiedene spezifische C ++ - Besetzungen (mit Erweiterungen)
reinterpret_cast
. ( [expr.cast] /4.4 ) Die zuvor versuchten Castsreinterpret_cast
sindconst_cast
undstatic_cast
(mit Erweiterungen und auch in Kombination), aber keiner von diesen kannstd::nullptr_t
zu einem integralen Typ gegossen werden .Sollte aber erfolgreich
reinterpret_cast<my_time_t>(nullptr)
sein, weil [expr.reinterpret.cast] / 4 besagt, dass ein Wert vom Typstd::nullptr_t
wie von in einen ganzzahligen Typ konvertiert werden kannreinterpret_cast<my_time_t>((void*)0)
, was möglich ist, weilmy_time_t = std::uintptr_t
ein Typ groß genug sein sollte, um alle Zeigerwerte darzustellen, und unter dieser Bedingung der Der gleiche Standardabsatz ermöglicht die Konvertierungvoid*
in einen integralen Typ.Es ist besonders seltsam, dass MSVC die Konvertierung zulässt, wenn die Cast-Notation anstelle der funktionalen Notation verwendet wird:
quelle
static_cast
insbesondere in einigen Fällen die Gussleiter im C-Stil abgefangen werden soll (z. B. ist ein Guss im C-Stil auf eine mehrdeutige Basis eher eine Fehlformstatic_cast
als einereinterpret_cast
), aber hier gilt keine.my_time_t(nullptr)
ist per Definition dasselbe wie(my_time_t)nullptr
, also ist MSVC sicherlich falsch, das eine zu akzeptieren und das andere abzulehnen.Obwohl ich in diesem Arbeitsentwurf C ++ Standard (ab 2014) nicht ausdrücklich erwähnen kann, dass die Konvertierung von einem integralen Typ verboten ist, gibt es auch keine Erwähnung, dass eine solche Konvertierung zulässig ist!
std::nullptr_t
Der Fall der Umstellung von
std::nullptr_t
aufbool
wird jedoch ausdrücklich erwähnt:Die einzige Stelle in diesem Dokumententwurf, an der die Konvertierung von
std::nullptr_t
einem integralen Typ erwähnt wird, befindet sich im Abschnitt "reinterpret_cast":Aus diesen beiden Beobachtungen könnte man (IMHO) vernünftigerweise vermuten, dass der
MSVC
Compiler korrekt ist.EDIT : Ihre Verwendung der "funktionalen Notation Cast" kann jedoch tatsächlich das Gegenteil suggerieren! Der
MSVC
Compiler hat kein Problem mit der Verwendung eines C-Stils, zum Beispiel:aber (wie in Ihrem Code) beschwert es sich darüber:
Aus demselben Standardentwurf:
Der "entsprechende Besetzungsausdruck (5.4)" kann sich auf eine Besetzung im C-Stil beziehen.
quelle
Alle sind standardkonform (siehe Entwurf n4659 für C ++).
nullptr
ist in [lex.nullptr] definiert als:Auch wenn Noten nicht normativ sind, macht dies eine deutlich , dass für den Standard,
nullptr
erwartet wird , auf einen Null umgewandelt werden Zeigerwert.Wir finden später in [conv.ptr]:
Auch hier verlangt der Standard, dass er
0
in astd::nullptr_t
undnullptr
in einen beliebigen Zeigertyp konvertiert werden kann.Ich lese, dass der Standard keine Anforderung hat, ob
nullptr
er direkt in einen integralen Typ konvertiert werden kann oder nicht. Von diesem Punkt aus:void *
beteiligt.quelle
nullptr
nicht, weil sie den nicht integralen Typ habenstd::nullptr_t
. 0 kann in einenstd::nullptr_t
Wert konvertiert werden, jedoch nicht in das Literalnullptr
. Dies ist alles beabsichtigt,std::nullptr_t
ist ein eingeschränkterer Typ, um unbeabsichtigte Konvertierungen zu verhindern.