web-dev-qa-db-fra.com

En quoi consiste exactement nullptr?

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

Eh bien, plus besoin de la macro NULL.

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

Cependant, je ne comprends pas comment nullptr fonctionne. Par exemple, article Wikipedia dit:

C++ 11 corrige cela en introduisant un nouveau mot-clé pour servir de constante de pointeur null distinguée: nullptr. Il s'agit de type nullptr_t, qui est implicitement convertible et comparable à tout type de pointeur ou de type 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?

Aussi, avez-vous un autre exemple (à côté de celui de Wikipedia) où nullptr est supérieur au bon vieux 0?

519
AraK

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

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

  • 4.10 à propos de la conversion de pointeur indique qu'une prvalue de type std::nullptr_t est une constante de pointeur null et qu'une constante de pointeur null 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. Passer NULL ou 0 sélectionnerait la version int avec confusion. 

  • Une conversion de nullptr_t en type intégral nécessite un reinterpret_cast et a la même sémantique qu'une transformation de (void*)0 en un type intégral (implémentation de mappage définie). Un reinterpret_cast ne peut pas convertir nullptr_t en n'importe quel type de pointeur. Fiez-vous si possible à la conversion implicite ou utilisez static_cast

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

370

De nullptr: Un pointeur nul, sûr et de type coupé :

Le nouveau mot clé C++ 09 nullptr désigne une constante rvalue qui sert de littéral pointeur null universel, remplaçant le littéral buggy et faiblement typé 0 et la fameuse macro NULL. Ainsi, nullptr met fin à plus de 30 ans d'embarras, d'ambiguïté et de bugs. Les sections suivantes présentent la fonctionnalité nullptr et montrent comment il peut remédier aux maux de NULL et de 0.

Autres références:

53
nik

Lorsque vous avez une fonction pouvant recevoir des pointeurs sur plusieurs types, l'appel avec NULL est ambigu. La façon dont cela fonctionne à présent est très compliquée en acceptant un int et en supposant que ce soit NULL.

template <class T>
class ptr {
    T* p_;
    public:
        ptr(T* p) : p_(p) {}

        template <class U>
        ptr(U* u) : p_(dynamic_cast<T*>(u)) { }

        // Without this ptr<T> p(NULL) would be ambiguous
        ptr(int null) : p_(NULL)  { assert(null == NULL); }
};

Dans C++11, vous pourrez surcharger nullptr_t de sorte que ptr<T> p(42); soit une erreur de compilation plutôt qu'une exécution assert.

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

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

Expert C++ Alex Allain le dit parfaitement ici :

"... imaginons que vous ayez les deux déclarations de fonction suivantes:

void func(int n); 
void func(char *s);

func( NULL ); // guess which function gets called?

Bien qu'il semble que la deuxième fonction soit appelée - vous passez, après tout, dans ce qui semble être un pointeur - c'est vraiment la première fonction qui sera appelée! Le problème est que, puisque NULL est égal à 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, n'arrive pas tout le temps, mais quand cela arrive, c'est extrêmement frustrant et déroutant. Si vous ne connaissez pas les détails de ce qui se passe, cela pourrait ressembler à un bogue du compilateur. Une fonctionnalité de langage qui ressemble à un bogue du compilateur n'est pas quelque chose que vous voulez.

Entrez nullptr. En C++ 11, nullptr est un nouveau mot clé qui peut (et devrait!) Être utilisé pour représenter les pointeurs NULL; en d’autres termes, où que vous écriviez NULL auparavant, vous devriez utiliser nullptr à la place. Ce n'est pas plus clair pour vous, le programmeur, (tout le monde sait ce que veut dire NULL), mais c'est plus explicite pour le compilateur, qui ne verra plus les 0 partout être utilisés pour avoir une signification spéciale lorsqu'ils sont utilisés comme un pointeur. "

17
Gabriel Staples

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

Je pense que cette distinction est 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.

8
user633658

Aussi, avez-vous un autre exemple (à côté de celui de Wikipedia) où nullptr est supérieur au bon vieux 0?

Oui. C'est aussi un exemple du monde réel (simplifié) qui s'est produit dans notre code de production. Cela se démarquait uniquement parce que gcc pouvait émettre un avertissement lors de la compilation croisée sur une plate-forme avec une largeur de registre différente (vous ne savez toujours pas exactement pourquoi uniquement lors de la compilation croisée de x86_64 à x86, avertit warning: converting to non-pointer type 'int' from NULL):

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

#include <iostream>

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;
}

Cela donne cette sortie:

(a == pb): 1
(a == 0): 0
(a == NULL): 0
(a == null): 1
4
Gabriel Schreiber

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

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

Il s'agit en fait d'une comparaison assez étroite, car None est généralement utilisé pour quelque chose qui n'a pas été initialisé, mais dans le même temps, les comparaisons telles que None == 0 sont fausses.

D'autre part, en clair, NULL == 0 renverrait la valeur IIRC car NULL est simplement une macro renvoyant 0, qui est toujours une adresse non valide (AFAIK).

3
Mark Rushakoff

C'est un mot clé car la norme le précisera en tant que tel. ;-) Selon le dernier projet public (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 se convertit pas implicitement en une valeur intégrale. 

3
KTC

Disons que vous avez une fonction (f) qui est surchargée pour prendre int et char *. Avant C++ 11, si vous vouliez l'appeler avec un pointeur null et que vous utilisiez NULL (c'est-à-dire la valeur 0), vous appelleriez celui 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 tu voulais. C++ 11 résout ce problème avec nullptr; Maintenant, vous pouvez écrire ce qui suit:

void g()
{
  f(nullptr); //calls f(char*)
}
2
Amit G.

0 était auparavant la seule valeur entière pouvant être utilisée en tant qu'initialiseur sans conversion pour les pointeurs: vous ne pouvez pas initialiser les pointeurs avec d'autres valeurs entières sans une conversion . Vous pouvez considérer 0 comme un singleton consexpr syntaxiquement similaire à un entier. littéral. Il peut initier n’importe quel pointeur ou entier. Mais étonnamment, vous constaterez qu’il n’a pas de type distinct: c’est un int. Alors, comment se fait-il que 0 puisse initialiser des pointeurs et 1 ne le puisse pas? Une réponse pratique était que nous avions besoin d'un moyen de définir la valeur null du pointeur et la conversion implicite directe de int en pointeur est sujette aux erreurs. Ainsi, 0 est devenu une véritable bête bizarre sortant de l’ère préhistorique .nullptr a été proposé comme une véritable représentation singleton constexpr de valeur nulle pour initialiser les pointeurs. Il ne peut pas être utilisé pour initialiser directement des entiers 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 semblait sémantiquement être un composant principal manquant .NULL est maintenant obsolète en faveur de nullptr, sauf si une bibliothèque décide de le définir en tant que nullptr.

0
Red.Wave