Zunächst möchte ich Ihnen eine Implementierung von einfachen nullptr_t
struct nullptr_t
{
void operator&() const = delete; // Can't take address of nullptr
template<class T>
inline operator T*() const { return 0; }
template<class C, class T>
inline operator T C::*() const { return 0; }
};
nullptr_t nullptr;
nullptr
ist ein subtiles Beispiel für Rückgabe Typ Resolver Idiom, um automatisch einen Null-Zeiger des richtigen Typs abzuleiten, abhängig vom Typ der Instanz, der er zugewiesen wird.
int *ptr = nullptr; // OK
void (C::*method_ptr)() = nullptr; // OK
- Wie Sie oben sehen können, wenn
nullptr
einem Integer-Zeiger zugewiesen wird, ein int
Typinstanziierung der schablonenhaften Konvertierungsfunktion erstellt wird. Dasselbe gilt auch für Methodenzeiger.
- Auf diese Weise, indem wir die Vorlagenfunktionalität nutzen, erstellen wir jedes Mal, wenn wir eine neue Typzuweisung vornehmen, den entsprechenden Typ des Nullzeigers.
- Als
nullptr
ein Integer-Literal mit dem Wert Null ist, können Sie seine Adresse nicht verwenden, was wir durch Löschen des &-Operators erreicht haben.
Warum brauchen wir nullptr
in erster Linie?
- Sie sehen traditionelle
NULL
hat einige Probleme damit, wie unten beschrieben:
1 Implizite Umwandlung
char *str = NULL; // Implicit conversion from void * to char *
int i = NULL; // OK, but `i` is not pointer type
2 Mehrdeutigkeit von Funktionsaufrufen
void func(int) {}
void func(int*){}
void func(bool){}
func(NULL); // Which one to call?
-
Bei der Kompilierung tritt der folgende Fehler auf:
error: call to 'func' is ambiguous
func(NULL);
^~~~
note: candidate function void func(bool){}
^
note: candidate function void func(int*){}
^
note: candidate function void func(int){}
^
1 error generated.
compiler exit status 1
3 Überlastung des Konstruktors
struct String
{
String(uint32_t) { /* size of string */ }
String(const char*) { /* string */ }
};
String s1( NULL );
String s2( 5 );
- In solchen Fällen benötigen Sie einen expliziten Cast (d.h.,
String s((char*)0))
.