web-dev-qa-db-fra.com

Pourquoi ne puis-je pas stocker des références dans un `std :: map` en C ++?

Je comprends que les références ne sont pas des pointeurs, mais un alias vers un objet. Cependant, je ne comprends toujours pas ce que cela signifie exactement pour moi en tant que programmeur, c'est-à-dire quelles sont les références sous le capot?

Je pense que la meilleure façon de comprendre cela serait de comprendre pourquoi je ne peux pas stocker une référence dans une carte.

Je sais que je dois cesser de considérer les références comme des mots syntaxiques par rapport aux pointeurs, mais je ne sais pas comment: /

42
ng5000

Comme je le comprends, les références sont implémentées comme des pointeurs sous le capot. La raison pour laquelle vous ne pouvez pas les stocker dans une carte est purement sémantique; vous devez initialiser une référence lorsqu'elle est créée et vous ne pouvez plus la modifier par la suite. Cela ne correspond pas au fonctionnement d'une carte.

28
Sebastiaan M

Vous devriez considérer une référence comme un "pointeur const vers un objet non const":

MyObject& ~~ MyObject * const

De plus, une référence ne peut être construite que comme un alias de quelque chose qui existe (ce qui n'est pas nécessaire pour un pointeur, bien que conseillé en dehors de NULL). Cela ne garantit pas que l'objet restera autour (et en effet vous pourriez avoir un noyau lors de l'accès à un objet via une référence s'il n'est plus), considérez ce code:

// Falsifying a reference
MyObject& firstProblem = *((MyObject*)0);
firstProblem.do(); // undefined behavior

// Referencing something that exists no more
MyObject* anObject = new MyObject;
MyObject& secondProblem = *anObject;
delete anObject;
secondProblem.do(); // undefined behavior

Maintenant, il y a deux exigences pour un conteneur STL:

  • T doit être constructible par défaut (une référence ne l'est pas)
  • T doit être assignable (vous ne pouvez pas réinitialiser une référence, mais vous pouvez l'assigner à son arbitre)

Donc, dans les conteneurs STL, vous devez utiliser des proxys ou des pointeurs.

Maintenant, l'utilisation de pointeurs peut s'avérer problématique pour la gestion de la mémoire, vous devrez donc peut-être:

N'UTILISEZ PAS auto_ptr , il y a un problème d'affectation car cela modifie l'opérande de droite.

J'espère que ça aide :)

25
Matthieu M.

La différence importante en dehors du sucre syntaxique est que les références ne peuvent pas être modifiées pour se référer à un autre objet que celui avec lequel elles ont été initialisées. C'est pourquoi ils ne peuvent pas être stockés dans des cartes ou d'autres conteneurs, car les conteneurs doivent pouvoir modifier le type d'élément qu'ils contiennent.

Pour illustrer cela:

A anObject, anotherObject;
A *pointerToA=&anObject;
A &referenceToA=anObject;

// We can change pointerToA so that it points to a different object
pointerToA=&anotherObject;

// But it is not possible to change what referenceToA points to.
// The following code might look as if it does this... but in fact,
// it assigns anotherObject to whatever referenceToA is referring to.
referenceToA=anotherObject;
// Has the same effect as
// anObject=anotherObject;
7
Martin B

en fait, vous pouvez utiliser des références dans une carte. Je ne le recommande pas pour les grands projets car cela pourrait provoquer des erreurs de compilation étranges mais:

    map<int, int&> no_prob;
    int refered = 666;
    no_prob.insert(std::pair<int, int&>(0, refered)); // works
    no_prob[5] = 777; //wont compile!!! 
    //builds default for 5 then assings which is a problem
    std::cout << no_prob[0] << std::endl; //still a problem
    std::cout << no_prob.at(0) << std::endl; //works!!

vous pouvez donc utiliser la carte mais il sera difficile de garantir qu'elle sera utilisée correctement, mais je l'ai utilisée pour les petits codes (généralement compétitifs)

5
Eytan

Un conteneur qui stocke une référence has pour initialiser tous ses éléments une fois construit et est donc moins utile.

struct container
{
   string& s_;           // string reference
};

int main()
{
   string s { "hello" };
   //container {};       // error - object has an uninitialized reference member
   container c { s };    // Ok
   c.s_ = "bye";
   cout << s;            // prints bye
}

De plus, une fois initialisé, le stockage des éléments de conteneur ne peut pas être modifié. s_ sera toujours fait référence au stockage de s ci-dessus.

2
tcb

Cet article explique comment les pointeurs sont implémentés sous le capot - http://www.codeproject.com/KB/cpp/References_in_c__.aspx , qui prend également en charge la réponse des sébastiens.

0