web-dev-qa-db-fra.com

Pourquoi ne puis-je pas créer un vecteur de références?

Quand je fais ça:

std::vector<int> hello;

Tout fonctionne très bien. Cependant, quand je fais un vecteur de références à la place:

std::vector<int &> hello;

J'ai des erreurs horribles comme 

erreur C2528: 'pointeur': le pointeur vers la référence est illégal

Je veux mettre un tas de références aux structures dans un vecteur, pour ne pas avoir à me mêler des pointeurs Pourquoi le vecteur lance-t-il une crise à ce sujet? Est-ce que ma seule option est d'utiliser un vecteur de pointeurs?

291
Colen

Le type de composant des conteneurs, tels que les vecteurs, doit être assignable . Les références ne sont pas assignables (vous ne pouvez les initialiser qu'une seule fois lorsqu'elles sont déclarées et vous ne pouvez pas les faire référencer autre chose plus tard). D'autres types non assignables ne sont pas non plus autorisés en tant que composants de conteneurs, par exemple. vector<const int> n'est pas autorisé.

282
newacct

oui vous pouvez, cherchez std::reference_wrapper , qui imite une référence mais qui est assignable et qui peut aussi être "réinstallé"

95
Ion Todirel

De par leur nature, les références ne peuvent être définies qu’au moment de leur création; c'est-à-dire que les deux lignes suivantes ont des effets très différents:

int & A = B;   // makes A an alias for B
A = C;         // assigns value of C to B.

De plus, c'est illégal:

int & D;       // must be set to a int variable.

Cependant, lorsque vous créez un vecteur, il n’est pas possible d’attribuer des valeurs à ses éléments lors de la création. Vous ne faites en gros que le dernier exemple.

27
James Curran

Ion Todirel a déjà mentionné une réponseOUIen utilisant std::reference_wrapper. Depuis C++ 11 nous avons un mécanisme pour extraire un objet de std::vector et supprimer la référence en utilisant std::remove_reference. Vous trouverez ci-dessous un exemple compilé avec g++ et clang avec option.
-std=c++11 et exécuté avec succès.

#include <iostream>
#include <vector>
#include<functional>

class MyClass {
public:
    void func() {
        std::cout << "I am func \n";
    }

    MyClass(int y) : x(y) {}

    int getval()
    {
        return x;
    }

private: 
        int x;
};

int main() {
    std::vector<std::reference_wrapper<MyClass>> vec;

    MyClass obj1(2);
    MyClass obj2(3);

    MyClass& obj_ref1 = std::ref(obj1);
    MyClass& obj_ref2 = obj2;

    vec.Push_back(obj_ref1);
    vec.Push_back(obj_ref2);

    for (auto obj3 : vec)
    {
        std::remove_reference<MyClass&>::type(obj3).func();      
        std::cout << std::remove_reference<MyClass&>::type(obj3).getval() << "\n";
    }             
}
22
Steephen

boost::ptr_vector<int> fonctionnera.

Edit: suggère d'utiliser std::vector< boost::ref<int> >, ce qui ne fonctionnera pas car vous ne pouvez pas construire par défaut un boost::ref.

14
Drew Dormann

C'est une faille dans le langage C++. Vous ne pouvez pas prendre l'adresse d'une référence, car tenter de le faire aboutirait à l'adresse de l'objet référencé et vous ne pouvez donc jamais obtenir un pointeur sur une référence. std::vector fonctionne avec des pointeurs sur ses éléments, donc les valeurs stockées doivent pouvoir être pointées. Vous devrez utiliser des pointeurs à la place.

12
Adam Rosenfield

Comme d'autres l'ont mentionné, vous utiliserez probablement plutôt un vecteur de pointeurs.

Cependant, vous pouvez utiliser un ptr_vector à la place!

2
Martin Cote

TL; DR

Utilisez std::reference_wrapper comme ceci:

#include <functional>
#include <string>
#include <vector>
#include <iostream>

int main()
{
    std::string hello = "Hello, ";
    std::string world = "everyone!";
    typedef std::vector<std::reference_wrapper<std::string>> vec_t;
    vec_t vec = {hello, world};
    vec[1].get() = "world!";
    std::cout << hello << world << std::endl;
    return 0;
}

Demo

Longue réponse

Comme standard suggère , pour un conteneur standard Xcontenant des objets de type Tname__, Tdoit être Erasablede Xname__.

Erasablesignifie que l'expression suivante est bien formée:

allocator_traits<A>::destroy(m, p)

Aest le type d'allocateur du conteneur, mest une instance d'allocateur et pest un pointeur de type *T. Voir ici pour la définition de Erasablename__.

Par défaut, std::allocator<T> est utilisé comme allocateur de vecteur. Avec l'allocateur par défaut, l'exigence est équivalente à la validité de p->~T() (notez que Test un type de référence et pest un pointeur sur une référence). Cependant, le pointeur sur une référence est illégal , par conséquent l'expression n'est pas bien formée.

1
ivaigult

Comme le suggèrent les autres commentaires, vous ne pouvez utiliser que des pointeurs . Mais si cela vous aide, voici une technique pour éviter de vous confronter directement aux pointeurs.

Vous pouvez faire quelque chose comme ce qui suit:

vector<int*> iarray;
int default_item = 0; // for handling out-of-range exception

int& get_item_as_ref(unsigned int idx) {
   // handling out-of-range exception
   if(idx >= iarray.size()) 
      return default_item;
   return reinterpret_cast<int&>(*iarray[idx]);
}
0
Omid