2009-05-28 21 views
249

Cuando hago esto:¿Por qué no puedo hacer un vector de referencias?

std::vector<int> hello; 

Todo funciona muy bien. Sin embargo, cuando yo le haga un vector de referencias en su lugar:

std::vector<int &> hello; 

recibo errores horribles como "error C2528: 'puntero': puntero para referenciar es ilegal".

Quiero poner un montón de referencias a las estructuras en un vector, para que no tenga que interferir con los punteros. ¿Por qué el vector lanza una rabieta sobre esto? ¿Mi única opción es utilizar un vector de punteros?

+34

puede utilizar std :: vector > hola; Ver http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=217 –

Respuesta

11

Es un defecto en el lenguaje C++. No puede tomar la dirección de una referencia, ya que intentar hacerlo daría como resultado la dirección del objeto al que se hace referencia y, por lo tanto, nunca podrá obtener un puntero a una referencia. std::vector funciona con punteros a sus elementos, por lo que los valores almacenados deben poder ser apuntados. Deberás usar punteros en su lugar.

+0

Supongo que podría implementarse utilizando un búfer void * y una ubicación nueva. No es que esto tenga mucho sentido. – peterchen

+46

"Defecto en el idioma" es demasiado fuerte. Es por diseño. No creo que se requiera que el vector funcione con punteros a elementos. Sin embargo, se requiere que el elemento sea asignable. Las referencias no son –

+0

No puede tomar el 'sizeof' como referencia. –

26

Por su propia naturaleza, las referencias solo se pueden establecer en el momento en que se crean; es decir, las dos líneas siguientes tienen efectos muy diferentes:

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

Futher, esto es ilegal:

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

Sin embargo, cuando se crea un vector, no hay manera de asignar valores a que es artículos en creación. Básicamente estás haciendo un montón del último ejemplo.

+6

"cuando creas un vector, no hay manera de asignar valores a sus elementos en la creación" No entiendo lo que quieres decir con esta afirmación. ¿Cuáles son "sus artículos en la creación"? Puedo crear un vector vacío Y puedo agregar elementos con .push_back(). Acaba de señalar que las referencias no son predecibles. Pero definitivamente puedo tener vectores de clases que no sean predecibles. – newacct

+1

No es necesario que el elemento ype de std :: vector sea predecible. Puedes escribir struct A {A (int); privado: A(); }; vector a; bien, siempre y cuando no uses esos métodos que requieren que sea predeterminado construible (como v.resize (100); - pero en su lugar necesitarás hacer v.resize (100, A (1));) –

+0

¿Y cómo escribiría un push_back() en este caso? Todavía usará asignación, no construcción. –

250

El tipo de componente de contenedores como vectores debe ser assignable. Las referencias no son asignables (solo puede inicializarlas una vez cuando se declaran, y no puede hacer que hagan referencia a otra cosa más adelante). Otros tipos no asignables tampoco están permitidos como componentes de contenedores, p. vector<const int> no está permitido.

+1

¿Estás diciendo que no puedo tener un vector de vectores? (Estoy seguro de que lo he hecho ...) –

+6

Sí, un std :: vector > es correcto, std :: vector es asignable. –

+14

De hecho, esta es la razón "real". El error acerca de que T * es imposible de T es U y es solo un efecto secundario del requisito violado de que T debe ser asignable. Si el vector fue capaz de verificar con precisión el parámetro de tipo, entonces probablemente diga "requisito violado: T & no asignable" –

14

boost::ptr_vector<int> funcionará.

Editar: fue una sugerencia de utilizar std::vector< boost::ref<int> >, que no funcionará porque no se puede construir un defecto-boost::ref.

+7

Pero puede tener un vector o tipos no construibles por defecto, ¿verdad? Solo debe tener cuidado de no utilizar el ctor predeterminado. del vector – Manuel

+1

@Manuel: O 'resize'. –

+5

Tenga cuidado, los contenedores de puntero de Boost toman la propiedad exclusiva de los puntos. [Cita] (http://www.boost.org/doc/libs/1_57_0/libs/ptr_container/doc/ptr_container.html # motivación): "Cuando necesitas semántica compartida, esta biblioteca no es lo que necesitas". –

2

Como han mencionado otros, probablemente termines utilizando un vector de punteros.

Sin embargo, es posible que desee considerar el uso de ptr_vector en su lugar.

+3

Esta respuesta no es viable, ya que se supone que un ptr_vector es un almacenamiento. Es decir, eliminará los punteros al eliminarlos. Entonces no es útil para su propósito. –

74

sí se puede, busque std::reference_wrapper, que imita una referencia, pero es asignable y también se puede "volver a colocar"

+1

¿Hay alguna forma de evitar llamar a 'get()' primero cuando intenta acceder a un método de una instancia de una clase en este contenedor? P.ej. 'reference_wrapper my_ref (...); my_ref.get(). doStuff(); 'no es muy similar a la referencia. – timdiels

+3

¿No es irrevocablemente convertible en el propio Tipo al devolver la referencia? – WorldSEnder

16

Ion Todirel ya se ha mencionado una respuesta usando std::reference_wrapper. Desde C++ 11 tenemos un mecanismo para recuperar objetos de std::vector y eliminar la referencia utilizando std::remove_reference. A continuación se muestra un ejemplo compilado usando g++ y clang con la opción
-std=c++11 y se ejecutó con éxito.

#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"; 
    }    
} 
+5

No veo el valor en 'std :: remove_reference <>' aquí. El punto de 'std :: remove_reference <>' es permitirle escribir "el tipo T, pero sin ser una referencia si es uno". Entonces 'std :: remove_reference :: type' es lo mismo que escribir' MyClass'. – alastair

+0

Sin ningún valor en absoluto; solo puede escribir ** 'para (MyClass obj3: vec) std :: cout << obj3.getval() <<" \ n ";' ** (or 'for (const MyClass y obj3: vec) 'si declaras' getval() 'const, como deberías). –

0

Como sugieren los demás comentarios, usted se limita a usar punteros. Pero si ayuda, aquí hay una técnica para evitar enfrentar directamente con punteros.

Usted puede hacer algo como lo siguiente:

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

'reinterpret_cast' no es necesario – Xeverous

Cuestiones relacionadas