445

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?

Jonas
  • 121,568
  • 97
  • 310
  • 388
Colen
  • 13,428
  • 21
  • 78
  • 107
  • 51
    you can use std::vector > hello; See http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=217 – amit kumar Aug 18 '09 at 15:53
  • 6
    @amit the link is valid no longer, official documentation [here](https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper) – Martin Apr 28 '19 at 16:25

11 Answers11

424

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.

laike9m
  • 18,344
  • 20
  • 107
  • 140
newacct
  • 119,665
  • 29
  • 163
  • 224
  • 1
    Are you saying I can't have a vector of vectors? (I'm sure I've done that...) – James Curran May 28 '09 at 18:29
  • 11
    Yes, a std::vector< std::vector > is correct, std::vector is assignable. – Martin Cote May 28 '09 at 18:31
  • 20
    Indeed, this is the "actual" reason. The error about T* being impossible of T is U& is just a side-effect of the violated requirement that T must be assignable. If vector was able to precisely check the type parameter, then it would probably say "violated requirement: T& not assignable" – Johannes Schaub - litb May 28 '09 at 18:47
  • 2
    Checking the assignable concept at http://www.boost.org/doc/libs/1_39_0/doc/html/Assignable.html all operations except the swap are valid on references. – amit kumar Aug 18 '09 at 16:01
  • I think the component type must also be default-constructible, no? And references are not that either. – cdhowie May 01 '13 at 21:24
  • 1
    @cdhowie, no, the component type need not be default-constructible, as long as you don't call resize() or the vector constructor that takes a size. (I conclude this from trying it using g++, not from reading any spec) – Don Hatch Jul 02 '14 at 07:06
  • I had this issue trying to do: `vector&>` The solution was to work with: `vector*>` – Gubatron Sep 12 '16 at 19:02
  • @JohannesSchaub-litb I guess it could do like `static_assert>>` or something along those lines – Post Self Sep 10 '17 at 19:57
  • 13
    This is no longer true. Since C++11, the only operation-independent requirement for element is to be "Erasable", and reference is not. See https://stackoverflow.com/questions/33144419/stl-container-of-references-c11. – laike9m Jul 14 '18 at 07:02
  • `References are not assignable (you can only initialize them once when they are declared, and you cannot make them reference something else later).` Then why can I `for (item& i: myVector) i = foo;` ? I am assigning the reference to something else. – johnbakers Dec 08 '19 at 19:51
  • 2
    @johnbakers: You are modifying the thing the reference points to, not the reference itself. Modifying the reference itself would allow you to have the reference to point to something else, which is impossible with C++ references. – newacct Dec 09 '19 at 00:31
  • It could, of course, be a specialization that internally stores pointers and presents them as references in the public API. That's what `reference_wrapper` does. – Aykhan Hagverdili Dec 29 '22 at 15:24
156

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

user1438233
  • 1,153
  • 1
  • 14
  • 30
  • 4
    Is there a way of getting around calling `get()` first when trying to access a method of an instance of a class in this wrapper? E.g. `reference_wrapper my_ref(...); my_ref.get().doStuff();` is not very reference like. – Tim Diels Oct 17 '14 at 15:40
  • 3
    Isn't it implcitly castable to the Type itself by returning the reference? – WorldSEnder Nov 13 '14 at 12:52
  • 4
    Yes, but that requires a context that implies which conversion is required. Member access does not do that, hence the need for `.get()`. What *timdiels* wants is `operator.`; have a look at the latest proposals/discussions about that. – underscore_d Sep 30 '18 at 12:50
  • 1
    Note however that while `reference_wrapper` unlike the raw reference type can be "reseated", it still cannot be uninitialized, so it is not default constructible. That means that while one can construct `vector>{int1, int2, int3}`, one still cannot have a vector with with default constructed elements: `vector>(5)`. This isn't even caught by IDE (CLion), but fails compilation. – Ivan Oct 14 '20 at 07:42
  • 1
    I find it odd that this is declared in `` -- it seems more general than merely callable objects. – wcochran May 27 '21 at 14:56
43

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.

Timotheos
  • 405
  • 3
  • 8
James Curran
  • 101,701
  • 37
  • 181
  • 258
37

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.

ivaigult
  • 6,198
  • 5
  • 38
  • 66
35

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";
        }             
    }
Milan
  • 1,743
  • 2
  • 13
  • 36
Steephen
  • 14,645
  • 7
  • 40
  • 47
  • 19
    I don’t see the value in `std::remove_reference<>` here. The point of `std::remove_reference<>` is to allow you to write "the type T, but without being a reference if it is one". So `std::remove_reference::type` is just the same as writing `MyClass`. – al45tair Feb 18 '16 at 17:36
  • 3
    No value at all in that - you can just write **`for (MyClass obj3 : vec) std::cout << obj3.getval() << "\n";`** (or `for (const MyClass& obj3: vec)` if you declare `getval()` const, as you should). – Toby Speight May 24 '17 at 09:32
20

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.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • I guess it could be implemented using a void * buffer and placement new. Not that this would make much sense. – peterchen May 28 '09 at 18:19
  • 61
    "Flaw in the language" is too strong. It is by design. I don't think it is required that vector works with pointers to elements. However it is required that the element be assignable. References are not. – Brian Neal May 28 '09 at 19:28
  • You can't take the `sizeof` a reference either. – David Schwartz May 17 '16 at 00:11
  • 1
    you don't need a pointer to a reference, you have the reference, if you do need the pointer, just keep the pointer itself; there is no problem to be solved here –  Dec 03 '17 at 01:53
  • It could, of course, be a specialization that internally stores pointers and presents them as references in the public API. That's what `reference_wrapper` does. – Aykhan Hagverdili Dec 29 '22 at 15:25
15

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.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • 8
    But you can have a vector or non default-constructible types, right? You only have to be careful not to use the default ctor. of the vector – Manuel Feb 04 '10 at 12:05
  • 1
    @Manuel: Or `resize`. – Lightness Races in Orbit Jul 31 '14 at 11:00
  • 5
    Be careful, Boost's pointer containers take exclusive ownership of the pointees. [Quote](http://www.boost.org/doc/libs/1_57_0/libs/ptr_container/doc/ptr_container.html#motivation): "When you do need shared semantics, this library is not what you need." – Brandlingo Mar 06 '15 at 14:03
3

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!

Martin Cote
  • 28,864
  • 15
  • 75
  • 99
  • 4
    This answer is not workable, since a ptr_vector is supposed to be a storage. That is it will delete the pointers at removal. So it is not usable for his purpose. – Klemens Morgenstern Oct 03 '14 at 12:12
  • One way or another you have to store something somewhere! Docs: "Instances of std::reference_wrapper are objects (they can be copied or stored in containers)" – Mohammad Rahimi Sep 29 '21 at 12:45
1

I found the reason in C++14, "International Standard ISO/IEC 14882:2014(E) Programming Language C++"

[8.3.2-5.s1] There shall be no references to references, no arrays of references, and no pointers to references.

Yuhan Chen
  • 11
  • 1
0

There is no technical reason why you couldn't have a vector of references, it's just not a part of the API, presumably to keep it similar to an array. A specialization could easily be added that works with references, stores them as pointers internally and presents as references in the API:

vector<int&> ivref;
int i = 42;
ivref.push_back(i); // address of i is stored
ivref.front() = 43; // i is set to 43
ivref.push_back(44); // Marked deleted for rvalue references 
ivref.resize(10);    // Marked deleted for vector of references
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
-1

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]);
}
Omid
  • 199
  • 1
  • 7