Qu’est-ce que nullptr?

Nous avons maintenant C ++ 11 avec de nombreuses nouvelles fonctionnalités. Le nouveau nullptr est intéressant et déroutant (du moins pour moi).

Eh bien, plus besoin de la méchante macro NULL .

 int* x = nullptr; myclass* obj = nullptr; 

Pourtant, je ne comprends pas comment fonctionne nullptr . Par exemple, l’article de Wikipedia dit:

C ++ 11 corrige cela en introduisant un nouveau mot-clé pour servir de constante de pointeur nul distincte: nullptr. Il est de type nullptr_t , qui est implicitement convertible et comparable à tout type de pointeur ou type de pointeur à membre. Il n’est pas implicitement convertible ou comparable aux types intégraux, sauf pour bool.

Comment est-ce un mot clé et une instance d’un type?

En outre, avez-vous un autre exemple (à côté de celui de Wikipédia) où nullptr est supérieur au bon ancien 0 ?

    Comment est-ce un mot clé et une instance d’un type?

    Ce n’est pas surprenant. Les deux true et false sont des mots-clés et en tant que littéraux, ils ont un type ( bool ). nullptr est un littéral de type std::nullptr_t , et c’est une valeur (vous ne pouvez pas prendre l’adresse en utilisant & ).

    • 4.10 propos de la conversion de pointeur dit qu’une valeur de type std::nullptr_t est une constante de pointeur nul et qu’une constante de pointeur nul intégrale peut être convertie en std::nullptr_t . La direction opposée n’est pas autorisée. Cela permet de surcharger une fonction pour les pointeurs et les entiers, et de transmettre nullptr pour sélectionner la version du pointeur. NULL passez NULL ou 0 , sélectionnez la version int confusion.

    • Un nullptr_t de nullptr_t en un type intégral nécessite un nullptr_t et a la même sémantique qu’une dissortingbution de (void*)0 vers un type intégral (implémentation de mappage définie). Un nullptr_t ne peut pas convertir nullptr_t en un type de pointeur. static_cast à la conversion implicite si possible ou utilisez static_cast .

    • La norme exige que sizeof(nullptr_t) soit sizeof(void*) .

    De nullptr: un pointeur Null de type sécurisé et clair :

    Le nouveau mot clé nullptr C ++ 09 désigne une constante de valeur qui sert de littéral de pointeur null universel, remplaçant le littéral 0 mal défini et faiblement typé et la fameuse macro NULL. nullptr met ainsi fin à plus de 30 ans d’embarras, d’ambiguïté et de bugs. Les sections suivantes présentent la fonction nullptr et montrent comment elle peut remédier aux problèmes de NULL et de 0.

    Autres références:

    • WikiBooks , avec exemple de code.
    • Ici à Stack Overflow: Utilisez-vous NULL ou 0 (zéro) pour les pointeurs en C ++?
    • template
    • Groupe Google: comp.lang.c ++. Modéré – discussion sur le compilateur

    Lorsque vous avez une fonction qui peut recevoir des pointeurs vers plusieurs types, l’appel avec NULL est ambigu. La façon dont cela fonctionne maintenant est très complexe en acceptant un int et en supposant que ce soit NULL .

     template  class ptr { T* p_; public: ptr(T* p) : p_(p) {} template  ptr(U* u) : p_(dynamic_cast(u)) { } // Without this ptr p(NULL) would be ambiguous ptr(int null) : p_(NULL) { assert(null == NULL); } }; 

    En C++11 vous pourriez surcharger nullptr_t pour que ptr p(42); serait une erreur de compilation plutôt qu’une assertion au moment de l’exécution.

     ptr(std::nullptr_t) : p_(nullptr) { } 

    Pourquoi nullptr en C ++ 11? Qu’Est-ce que c’est? Pourquoi NULL n’est-il pas suffisant?

    Alex Allain, expert en C ++, le dit parfaitement ici :

    “… imaginez que vous avez les deux déclarations de fonction suivantes:

     void func(int n); void func(char *s); func( NULL ); // guess which function gets called? 

    Bien que la deuxième fonction semble s’appeler – vous êtes, après tout, en train de passer ce qui semble être un pointeur – c’est vraiment la première fonction qui sera appelée! Le problème est que parce que NULL vaut 0 et que 0 est un entier, la première version de func sera appelée à la place. C’est le genre de chose qui, oui, ne se produit pas tout le temps, mais quand cela arrive, c’est extrêmement frustrant et déroutant. Si vous ne connaissiez pas les détails de ce qui se passe, cela pourrait ressembler à un bogue de compilation. Une fonctionnalité de langage qui ressemble à un bogue de compilateur n’est pas ce que vous voulez.

    Entrez nullptr. En C ++ 11, nullptr est un nouveau mot-clé qui peut (et devrait!) Être utilisé pour représenter des pointeurs NULL; En d’autres termes, où que vous écriviez NULL auparavant, vous devriez plutôt utiliser nullptr. Ce n’est pas plus clair pour vous, le programmeur (tout le monde sait ce que NULL signifie), mais il est plus explicite pour le compilateur , qui ne verra plus les 0 partout avoir une signification particulière lorsqu’il est utilisé comme pointeur. ”

    nullptr ne peut pas être affecté à un integral type tel qu’un int mais uniquement un pointer type; soit un type de pointeur intégré tel que int *ptr ou un pointeur intelligent tel que std::shared_ptr

    Je crois que c’est une distinction importante car NULL peut toujours être affecté à un integral type et un pointer comme NULL est une macro étendue à 0 qui peut servir à la fois de valeur initiale pour un int et de pointer .

    En outre, avez-vous un autre exemple (à côté de celui de Wikipédia) où nullptr est supérieur au bon ancien 0?

    Oui. C’est aussi un exemple réel (simplifié) qui s’est produit dans notre code de production. Il se démarquait seulement parce que gcc était capable d’émettre un avertissement lors de la compilation croisée vers une plate-forme avec une largeur de registre différente (toujours pas sûr de savoir exactement quand la compilation de x86_64 à x86 avertit warning: converting to non-pointer type 'int' from NULL ) :

    Considérez ce code (C ++ 03):

     #include  struct B {}; struct A { operator B*() {return 0;} operator bool() {return true;} }; int main() { A a; B* pb = 0; typedef void* null_ptr_t; null_ptr_t null = 0; std::cout << "(a == pb): " << (a == pb) << std::endl; std::cout << "(a == 0): " << (a == 0) << std::endl; // no warning std::cout << "(a == NULL): " << (a == NULL) << std::endl; // warns sometimes std::cout << "(a == null): " << (a == null) << std::endl; } 

    Il donne cette sortie:

     (a == pb): 1 (a == 0): 0 (a == NULL): 0 (a == null): 1 

    C’est un mot-clé car le standard le spécifie comme tel. 😉 Selon la dernière version publique (n2914)

    2.14.7 Littéraux de pointeur [lex.nullptr]

     pointer-literal: nullptr 

    Le littéral de pointeur est le mot clé nullptr . C’est une valeur de type std::nullptr_t .

    C’est utile car il ne convertit pas implicitement en valeur intégrale.

    Eh bien, d’autres langues ont des mots réservés qui sont des instances de types. Python, par exemple:

     >>> None = 5 File "", line 1 SyntaxError: assignment to None >>> type(None)  

    C’est en fait une comparaison assez étroite car None est généralement utilisé pour quelque chose qui n’a pas été initialisé, mais en même temps, des comparaisons telles que None == 0 sont fausses.

    En revanche, dans la version C, NULL == 0 renvoie la valeur IIRC vraie car NULL est simplement une macro renvoyant 0, qui est toujours une adresse invalide (AFAIK).

    Disons que vous avez une fonction (f) qui est surchargée pour prendre à la fois int et char *. Avant C ++ 11, si vous vouliez l’appeler avec un pointeur nul et que vous utilisiez NULL (c.-à-d. La valeur 0), vous appeleriez celui qui est surchargé pour int:

     void f(int); void f(char*); void g() { f(0); // Calls f(int). f(NULL); // Equals to f(0). Calls f(int). } 

    Ce n’est probablement pas ce que vous vouliez. C ++ 11 résout ce problème avec nullptr; Maintenant, vous pouvez écrire ce qui suit:

     void g() { f(nullptr); //calls f(char*) } 

    0 était la seule valeur entière pouvant être utilisée comme initialiseur sans dissortingbution pour les pointeurs: vous ne pouvez pas initialiser les pointeurs avec d’autres valeurs entières sans conversion. Vous pouvez considérer 0 comme étant un dérivé de syntaxe syntaxiquement similaire à un littéral d’entier. Il peut initier n’importe quel pointeur ou entier. Mais étonnamment, vous verrez qu’il n’a pas de type distinct: c’est un int . Alors, comment se fait-il que 0 puisse initialiser des pointeurs et que 1 ne le soit pas? Une réponse pratique a été que nous avons besoin d’un moyen de définir la valeur null du pointeur et que la conversion implicite directe de int en pointeur est sujette à erreur. Ainsi, 0 est devenu une véritable bête bizarre hors de l’ère préhistorique. nullptr été proposé pour être une véritable représentation de constexpr singleton de valeur nulle pour initialiser des pointeurs. Il ne peut pas être utilisé pour initialiser des entiers directement et élimine les ambiguïtés impliquées dans la définition de NULL en termes de 0. nullptr pourrait être défini comme une bibliothèque utilisant la syntaxe std mais sémantiquement comme un composant principal manquant. NULL est maintenant déconseillé en faveur de nullptr , sauf si une bibliothèque décide de la définir comme nullptr .

    NULL n’a pas besoin d’être 0. Tant que vous utilisez toujours NULL et jamais 0, NULL peut avoir n’importe quelle valeur. En supposant que vous programmez un microcontrôleur von Neuman avec une mémoire plate, ses vektors d’interruption sont à 0. Si NULL est égal à 0 et que quelque chose est écrit à un pointeur NULL, le microcontrôleur se bloque. Si NULL est, disons 1024 et à 1024 il y a une variable réservée, l’écriture ne le plantera pas, et vous pourrez détecter les assignations de pointeur NULL depuis l’intérieur du programme. Ceci est inutile sur les PC, mais pour les sondes spatiales, les équipements militaires ou médicaux, il est important de ne pas tomber en panne.