Question

Pourquoi est-il impossible d'avoir une référence au vide? La seule chose que j'ai trouvée dans la norme C ++ est cette ligne, sous 8.3.2.1

  

Un déclarateur qui spécifie le type & "référence à cv void &"; est mal formé.

Pourquoi est-ce ainsi? Pourquoi ne puis-je pas écrire un & "Générique &"; fonction qui accepte un void&?

Soyons clairs: je n'ai aucune application utile à l'esprit. Il serait peut-être préférable d'utiliser une référence au vide que d'utiliser des modèles, mais je suis simplement curieux de savoir pourquoi il est interdit d'interdire cette construction.

Pour clarifier un peu, je comprends que l’utilisation d’une référence à vide & "telle quelle &"; serait aussi dépourvu de sens que déréférencer un pointeur sur un vide. Cependant, je pourrais le convertir en type de référence afin de l'utiliser, n'est-ce pas? En fait, je ne vois pas pourquoi l'extrait suivant peut fonctionner ...

void foo(void *data)
{
    int *i = reinterpret_cast<int*>(data);
    // do something with i
}

... alors que celui-ci ne peut pas:

void foo(void &data)
{
    int &i = reinterpret_cast<int&>(data);
    // do something with i
}
Était-ce utile?

La solution

Si vous aviez une référence au vide, que feriez-vous avec? Ce ne serait ni un chiffre, ni un caractère, ni un pointeur, ni rien de ce genre. Votre fonction générique hypothétique ne peut effectuer aucune opération sur elle, si ce n’est en prenant son adresse (et non sa taille).

" void " a deux utilisations: refuser toute connaissance de type (comme dans void *), et ne rien spécifier par opposition à quelque chose (retour de fonction vide). Dans les deux cas, il n'est pas possible de dire quoi que ce soit à propos d'un vide, sauf qu'il peut avoir une adresse.

Si vous ne trouvez pas en quoi quelque chose peut être utile, mais je ne peux pas, c'est au moins la preuve que quelque chose est inutile, et cela pourrait bien être au moins une partie de la raison ici.

Autres conseils

Demandez-vous d’abord comment vous pourriez dé-référencer un pointeur vide?

void *p = /*something*/ ;
cout << *p << endl;

Le code ci-dessus n'a pas de sens. L'une des raisons de notre annulation est que nous pouvons dire & "J'ai besoin de faire un travail générique de pointeur ici, et je ne sais pas ni ne me soucie de ce que je pointe quot ;. Par définition, le compilateur ne sait pas sur quoi un vide * pointe, il ne peut donc pas le déréférencer. Vous pouvez le faire, mais le compilateur ne le peut pas.

Une référence à un vide souffre du même problème. Par définition, les données pointées n'ont pas de type, elles ne peuvent donc pas être référencées de manière significative.

Pour le référencer, vous - le programmeur - devez le transtyper vers un autre type, vous pouvez alors avoir une référence typée.

Je ne sais pas si j'ai expliqué cela aussi bien que je le voulais.

Ruben, des idées?

MODIFIER: pour répondre à votre modification.

Prenez la première fonction, où vous transmettez des données void *. les données sont un élément parfaitement valide, vous pouvez calculer avec, ou si vous avez implémenté une journalisation, vous pouvez le journaliser.

logger << data;

et vous obtiendrez les points de données d'adresse. Si vous essayez de déréférencer les données, le compilateur vous donnera une erreur (ne pas avoir le compilateur C ++ à la portée de la main, donc pas sûr de l'erreur réelle). par exemple.

void* data = /* some assignment */;
logger << *data; // compiler error.

Maintenant, le compilateur ne vous laissera pas déréférencer un vide * pour une raison quelconque (cela n’a aucun sens), il en va de même pour une référence à void & dans les données, sauf que parce que c’est une référence < em> il est implicitement déréférencé tout le temps . Le compilateur ne vous laissera pas déréférencer un vide * sur une opération, il ne vous laissera pas déréférencer constamment.

void& data = /* some assignment *.;
logger << data; // means same as logger << *data above

Vous ne pouvez pas faire RIEN pour que les données SAUF prennent son adresse, et il existe une méthode parfaitement bonne - et sûre - intégrée dans la langue pour le faire, à savoir

void* data;

Est-ce que cela a plus de sens?

Une référence est une référence à une instance de quelque chose. Une instance de quelque chose ne peut pas être de type void. Toute instance de quelque chose doit avoir un type spécifique (et éventuellement des types de base).

Voici un résumé des différentes choses qui ont été dites et auxquelles j'ai pensé.

Deux raisons principales pour lesquelles la référence à un vide est interdite

1 Ils auraient été totalement inutiles.

En effet, si nous regardons à l’époque de C, les pointeurs vides avaient deux objectifs:

  • Gestion de la mémoire (par exemple, malloc)
  • Générité (fonctions d'écriture acceptant n'importe quel type d'argument)

À la sortie de C ++, les modèles sont devenus la meilleure solution pour implémenter la généricité. Cependant, la gestion de la mémoire personnalisée devait encore être possible et l'interopérabilité entre C ++ et C était une préoccupation majeure. Void * a donc été conservé. Une référence vide hypothétique ne serait d'aucune utilité pour la gestion de la mémoire, et la généricité est déjà couverte; elle n'aurait donc pratiquement aucune utilité (à l'exception de la garantie de non-nullité décrite ci-dessous).

2 Vous ne pourriez rien faire avec

Lorsque vous utilisez un pointeur vide, vous n'êtes pas autorisé à le déréférencer. transposée au cas des références, cela signifie que vous ne pouvez pas utiliser la référence vide (toujours hypothétique). Donc

void *data = // something
// using *data and data-> is forbidden

void &data = // something
// using data is forbidden

Cependant, nous pourrions penser à un cas d'utilisation où la référence n'aurait pas besoin d'être & "déréférencée &"; (cette phrase est terriblement incorrecte, mais vous comprenez mon point), mais nous ne prenons que son adresse. Supposons que j’ai la fonction suivante:

void foo(void *dataptr)
{
    assert(dataptr != NULL); // or != 0
    // do something with dataptr
}

Pour éviter cette assertion agaçante, je pourrais écrire la fonction de cette façon:

void foo(void &dataref)
{
    void *data = &dataref;
    // do something with data
}

Toutefois, pour que cela fonctionne, &dataref doit être équivalent à dataptr, ce qui n'est pas le cas : &*dataptr est équivalent à <=>!

Par conséquent, même prendre l'adresse implique un déréférencement, du moins conceptuel (en coulisse, la première équivalence est probablement vraie, mais au niveau sémantique, ce n'est pas le cas). Par conséquent, nous ne pouvons absolument pas utiliser les données, les références vides sont donc une aberration.

Techniquement, tout ce qui est garanti est qu’une référence à un objet est un alias pour celui-ci. Que l'argument de référence sous le capot soit passé avec des pointeurs est un détail d'implémentation. Cela peut être déroutant à cause des références réutilisant le & Amp; opérateur qui est aussi une adresse de, mais gardez à l'esprit que l'opérateur a en fait des significations différentes dans des contextes différents (dans une déclaration de variable ou de paramètre, il s'agit d'un type de référence; sinon, il s'agit de l'adresse-de, sauf lorsqu'il s'agit de bitwise et). Du fait qu'il ne s'agit techniquement que d'un alias pour un objet, une référence est "toujours déréférencée", comme l'explique Worrier.

OK, il y a une chose qui me dérange à ce sujet. Comme mentionné ci-dessus, l'idée d'un void* est que vous disposez toujours d'une variable valide contenant une adresse, mais que le type est ignoré. Cela semble permis car nous pouvons toujours utiliser les données d'adresse - le type est quelque peu superflu (ou moins important) dans ce contexte. Déréférencer est mauvais, car essayer et accéder à un membre n'a pas de sens, par exemple. p.mem Nous ne savons pas à quelle classe faire référence, et donc à la mémoire à laquelle accéder, aux pointeurs vtable à suivre.

Cependant, il semblerait alors logique que p seul soit acceptable car il se réfère uniquement à l'objet, mais à aucune de ses données. Aucune information de classe n'est nécessaire pour le faire, juste l'adresse. Je comprends que cela ne sert à rien, mais il est important de définir quand les choses se détériorent. En autorisant cette notion, une référence C ++ (constamment déréférencée mais n’accédant rien), par ex. void& ref = static_cast< &void >(obj) a également un sens, et permettrait donc des références vides. Je ne dis pas que quiconque devrait en discuter avec les responsables, mais à partir d'un & "Faire sens" & "; point de vue, il semblerait correct, non?

Comme Luc Touraille l’a souligné plus haut (du moins, c’est mon interprétation), cela pourrait être mis en œuvre, mais le problème est sémantique. L'explication raisonnable à laquelle je pouvais arriver était que, depuis qu'une variable d'objet est une & "Balise &"; pour une séquence de mémoire, le type a une valeur sémantique importante. Ainsi, le pointeur, considéré comme une variable avec une valeur d'adresse, considère le type comme quelque peu superflu - ce n'est pas la clé pour le définir.

Quelqu'un serait-il d'accord avec cela?

Vous pouvez considérer une référence comme un pointeur dé-référencé. Syntaxiquement, vous traitez une référence comme s’il ne s’agissait pas d’un pointeur: vous n’avez pas besoin de l’opérateur * pour le déréférencer, vous pouvez l’utiliser. plutôt que - > accéder à ses membres.

Cependant, vous ne pouvez pas déréférencer un pointeur void. Comme le souligne Binary Worrier, essayer de le faire vous donnera une erreur de compilation. Et si vous ne pouvez pas avoir un pointeur d’annulation de référence déréférencé, cela signifie que vous ne pouvez pas avoir une référence d’annonce vide.

S'ils l'étaient, ils ne seraient pas sémantiquement différenciés des pointeurs et constitueraient un sucre syntaxique. Une référence indique, & "Je me réfère à quelque chose de ce type. &"; Autoriser une référence nulle ou nulle affaiblirait cette différence par rapport aux indicateurs.

Certes, il est toujours possible pour une référence de faire référence à un objet qui n'existe plus, mais il s'agit d'une exception.

Ce qui suit n'est pas une défense de la notion de référence vide. Je vous l'offre comme une anecdote sauvage. Demandez-vous si ça ne sent pas drôle.

Ma société a été l'une des premières entreprises à utiliser le C ++ à des fins commerciales et a été initialement compilée à l'aide de Cfront . Les premiers développeurs apprenaient toujours la langue et utilisaient généralement toutes les astuces du livre (les opérateurs du monde entier!). Voici un truc qu'ils pensaient être cool:

void Foo::something(int action, ostream &os = *(ostream *)0)
{
   ostream *os_p = &os;
   if (&os == (ostream *)0) {
      os_p = &cerr;
   }
   // continue with method
}

Donc, vous avez ici, non pas une référence vide, mais plutôt une référence typée avec une liaison potentiellement vide ! Un instant de réflexion devrait probablement suggérer de meilleures alternatives à cet idiome particulier ...

void est quelque chose qui, par définition, n'existe pas. Il n'est donc pas logique d'en avoir l'adresse.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top