Why can't I make a vector of references?
Solution 1
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
yes you can, look for std::reference_wrapper
, that mimics a reference but is assignable and also can be "reseated"
Solution 3
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
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
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;
}
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.
Comments
-
Colen about 2 years
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?
-
amit kumar almost 15 yearsyou can use std::vector<reference_wrapper<int> > hello; See informit.com/guides/content.aspx?g=cplusplus&seqNum=217
-
Martin about 5 years@amit the link is valid no longer, official documentation here
-
-
peterchen about 15 yearsI guess it could be implemented using a void * buffer and placement new. Not that this would make much sense.
-
James Curran about 15 yearsAre you saying I can't have a vector of vectors? (I'm sure I've done that...)
-
Martin Cote about 15 yearsYes, a std::vector< std::vector<int> > is correct, std::vector is assignable.
-
newacct about 15 years"when you create a vector, there is no way to assign values to it's items at creation" I don't understand what you mean by this statement. What are "its items at creation"? I can create an empty vector. And I can add items with .push_back(). You are just pointing out that references are not default-constructible. But I can definitely have vectors of classes that are not default-constructible.
-
Johannes Schaub - litb about 15 yearsThe element ype of std::vector<T> is not required to be default constructible. You can write struct A { A(int); private: A(); }; vector<A> a; just fine - as long as you don't use such methods that require it to be default constructible (like v.resize(100); - but instead you will need to do v.resize(100, A(1)); )
-
James Curran about 15 yearsAnd how would you write such a push_back() in this case? It will still use assignment, not construction.
-
Johannes Schaub - litb about 15 yearsJames Curran, No default construction takes place there. push_back just placement-news A into the preallocated buffer. See here: stackoverflow.com/questions/672352/… . Note that my claim is only that vector can handle non-default-constructible types. I don't claim, of course, that it could handle T& (it can't, of course).
-
Dai Doan about 15 years"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.
-
amit kumar almost 15 yearsChecking the assignable concept at boost.org/doc/libs/1_39_0/doc/html/Assignable.html all operations except the swap are valid on references.
-
Manuel over 14 yearsBut 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
-
cdhowie about 11 yearsI think the component type must also be default-constructible, no? And references are not that either.
-
Don Hatch almost 10 years@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)
-
Lightness Races in Orbit almost 10 years@Manuel: Or
resize
. -
Klemens Morgenstern over 9 yearsThis 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.
-
Tim Diels over 9 yearsIs 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<MyClass> my_ref(...); my_ref.get().doStuff();
is not very reference like. -
WorldSEnder over 9 yearsIsn't it implcitly castable to the Type itself by returning the reference?
-
Brandlingo over 9 yearsBe careful, Boost's pointer containers take exclusive ownership of the pointees. Quote: "When you do need shared semantics, this library is not what you need."
-
al45tair over 8 yearsI don’t see the value in
std::remove_reference<>
here. The point ofstd::remove_reference<>
is to allow you to write "the type T, but without being a reference if it is one". Sostd::remove_reference<MyClass&>::type
is just the same as writingMyClass
. -
David Schwartz about 8 yearsYou can't take the
sizeof
a reference either. -
Gubatron almost 8 yearsI had this issue trying to do:
vector<vector <int>&>
The solution was to work with:vector<vector<int>*>
-
Toby Speight about 7 yearsNo value at all in that - you can just write
for (MyClass obj3 : vec) std::cout << obj3.getval() << "\n";
(orfor (const MyClass& obj3: vec)
if you declaregetval()
const, as you should). -
Post Self almost 7 years@JohannesSchaub-litb I guess it could do like
static_assert<std::is_same_v<T, std::remove_reference<T>>>
or something along those lines -
Admin over 6 yearsyou 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
-
Xeverous over 6 years
reinterpret_cast
is not needed -
laike9m almost 6 yearsThis is no longer true. Since C++11, the only operation-independent requirement for element is to be "Erasable", and reference is not. See stackoverflow.com/questions/33144419/….
-
underscore_d over 5 yearsYes, 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 isoperator.
; have a look at the latest proposals/discussions about that. -
underscore_d over 5 yearsAs the other answers show, we are not confined to using pointers at all.
-
johnbakers over 4 years
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 Ifor (item& i: myVector) i = foo;
? I am assigning the reference to something else. -
newacct over 4 years@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.
-
Ivan over 3 yearsNote 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 constructvector<reference_wrapper<int>>{int1, int2, int3}
, one still cannot have a vector with with default constructed elements:vector<reference_wrapper<int>>(5)
. This isn't even caught by IDE (CLion), but fails compilation. -
wcochran about 3 yearsI find it odd that this is declared in
<functional>
-- it seems more general than merely callable objects. -
Mohammad Rahimi over 2 yearsOne 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)"