Why can't I make a vector of references?

C++VectorReferenceStlContainer Data-Type

C++ Problem Overview


When I do this:

std::vector<int> hello;

Everything works great. However, when I make it a vector of references instead:

std::vector<int &> hello;

I get horrible errors like

> error C2528: 'pointer' : pointer to reference is illegal

I want to put a bunch of references to structs into a vector, so that I don't have to meddle with pointers. Why is vector throwing a tantrum about this? Is my only option to use a vector of pointers instead?

C++ Solutions


Solution 1 - C++

The component type of containers like vectors must be assignable. References are not assignable (you can only initialize them once when they are declared, and you cannot make them reference something else later). Other non-assignable types are also not allowed as components of containers, e.g. vector<const int> is not allowed.

Solution 2 - C++

yes you can, look for std::reference_wrapper, that mimics a reference but is assignable and also can be "reseated"

Solution 3 - C++

By their very nature, references can only be set at the time they are created; i.e., the following two lines have very different effects:

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

Futher, this is illegal:

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

However, when you create a vector, there is no way to assign values to it's items at creation. You are essentially just making a whole bunch of the last example.

Solution 4 - C++

Ion Todirel already mentioned an answer YES using std::reference_wrapper. Since C++11 we have a mechanism to retrieve object from std::vector and remove the reference by using std::remove_reference. Below is given an example compiled using g++ and clang with option -std=c++11 and executed successfully.

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

Solution 5 - C++

TL; DR

Use std::reference_wrapper like this:

#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

Long answer

As standard suggests, for a standard container X containing objects of type T, T must be Erasable from X.

Erasable means that the following expression is well formed:

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

A is container's allocator type, m is allocator instance and p is a pointer of type *T. See here for Erasable definition.

By default, std::allocator<T> is used as vector's allocator. With the default allocator, the requirement is equivalent to the validity of p->~T() (Note the T is a reference type and p is pointer to a reference). However, pointer to a reference is illegal, hence the expression is not well formed.

Solution 6 - C++

It's a flaw in the C++ language. You can't take the address of a reference, since attempting to do so would result in the address of the object being referred to, and thus you can never get a pointer to a reference. std::vector works with pointers to its elements, so the values being stored need to be able to be pointed to. You'll have to use pointers instead.

Solution 7 - C++

boost::ptr_vector<int> will work.

Edit: was a suggestion to use std::vector< boost::ref<int> >, which will not work because you can't default-construct a boost::ref.

Solution 8 - C++

As other have mentioned, you will probably end up using a vector of pointers instead.

However, you may want to consider using a ptr_vector instead!

Solution 9 - C++

As the other comments suggest, you are confined to using pointers. But if it helps, here is one technique to avoid facing directly with pointers.

You can do something like the following:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionColenView Question on Stackoverflow
Solution 1 - C++newacctView Answer on Stackoverflow
Solution 2 - C++user90843View Answer on Stackoverflow
Solution 3 - C++James CurranView Answer on Stackoverflow
Solution 4 - C++SteephenView Answer on Stackoverflow
Solution 5 - C++ivaigultView Answer on Stackoverflow
Solution 6 - C++Adam RosenfieldView Answer on Stackoverflow
Solution 7 - C++Drew DormannView Answer on Stackoverflow
Solution 8 - C++Martin CoteView Answer on Stackoverflow
Solution 9 - C++OmidView Answer on Stackoverflow