Why Can't I Make a Vector of References

std::vector of references

There are some possibilities:

  1. Store a vector of pointers (use if your vectors share ownership of the pointers):

    std::vector<std::shared_ptr<Foo>> vA, vB;
  2. Store a vector of wrapped references (use if the vectors do not share ownership of the pointers, and you know the object referenced are valid past the lifetime of the vectors):

    std::vector<std::reference_wrapper<Foo>> vA, vB;
  3. Store a vector of raw pointers (use if your vectors do not share ownership of the pointers, and/or the pointers stored may change depending on other factors):

    std::vector<Foo*> vA, vB;

    This is common for observation, keeping track of allocations, etc. The usual caveats for raw pointers apply: Do not use the pointers to access the objects after the end of their life time.

  4. Store a vector of std::unique_ptr that wrap the objects (use if your vectors want to handover the ownership of the pointers in which case the lifetime of the referenced objects are governed by the rules of std::unique_ptr class):

    std::vector<std::unique_ptr<Foo>> vA, vB;

Standard Practice for Creating a Vector of References Using Only the Standard Libraries

I would like to create an object, put the object into a vector, and still be able to modify the same object by accessing only the vector. However, I understand that when an object is push_back() to a vector, the object is actually copied into the vector. As a result, accessing the object in the vector will merely access a similar, but different object.

I'm almost certain that this is not what you want or "should" want. Forgive me that direct opening of my answer, but unless you have a very good reason to do this, you probably don't want to do it.

For that - a vector with references - to work you must guarantee that the referenced objects won't get moved nor destructed while you hold references to them. If you have them in a vector, make sure that vector isn't resized. If you have them on the stack like in your example, then don't let the vector of references or a copy of it leave that stack frame. If you want to store them in some container, use a std::list (it's iterators - pointers basically - don't get invalidated when inserting or removing elements).

You already noticed that you cannot have a vector of "real" references. The reason therefore is that references aren't assignable. Consider following code:

int a = 42;
int b = 21;
int & x = a; // initialisation only way to bind to something
int & y = b;
x = y;
b = 0;

After that, the value you obtain from x will be 21, because the assignment didn't change the reference (to be bound to b) but the referenced object, a. But a std::vector explicitly requires this.

You could now set out and write an wrapper around a pointer like ...

template<typename T>
struct my_ref {
T * target;
// don't let one construct a my_ref without valid object to reference to
my_ref(T & t) : target(&t) {}
// implicit conversion into an real reference
operator T &(void) {
return *target;
}
// default assignment works as expected with pointers
my_ref & operator=(my_ref const &) = default;
// a moved from reference doesn't make sense, it would be invalid
my_ref & operator=(my_ref &&) = delete;
my_ref(my_ref &&) = delete;
// ...
};

... but this is pretty pointless since std::reference_wrapper already provides exactly that:

int main (int, char**) {
int object = 21; // half of the answer
vector<reference_wrapper<int>> v;
v.push_back(object);
v[0].get() = 42; // assignment needs explicit conversion of lhs to a real reference
cout << "the answer is " << object << endl;
return 0;
}

(Example live here)

Now one could argue why using a wrapper around a pointer like std::reference_wrapper when one could also directly use a pointer. IMO a pointer, having the ability to be nullptr, changes the semantics of the code: When you have a raw pointer, it could be invalid. Sure, you can just assume that it's not, or put it somewhere in comments, but in the end you then rely on something that's not guaranteed by the code (and this behaviour normally leads to bugs).

If an element of your vector could "reference" an object or be invalid, then still raw pointers aren't the first choice (for me): When you use an element from your vector which is valid, then the object referenced by it is actually referenced from multiple places on your code; it's shared. The "main" reference to the object then should be a std::shared_ptr and the elements of your vector std::weak_ptrs. You can then (thread safe) acquire a valid "reference" (a shared pointer) when you need to and drop it when done:

auto object = make_shared<int>(42);
vector<weak_ptr<int>> v;
v.push_back (object);

// ... somewhere later, potentially on a different thread
if (auto ref = v[0].lock()) {
// noone "steals" the object now while it's used here
}
// let them do what they want with the object, we're done with it ...

Finally, please take my answer with a grain of salt, much of it is based on my opinion (and experience) and might not count as "standard practice".

Vectors of references to objects

You can't have vector of references, as a reference is not copyable assignable and all STL containers are supposed to store copyable assignable items.

But you can make the container to hold pointers. Like this:

vector< Agents* > seenAgents;

This is a little dangerous. You need to be sure that these pointers will remain valid. I mean - if someone deletes an object, pointed by a pointer in this container, the pointer becomes invalid. You need to be sure that this will not happen, because you can't check it (you can't check for NULL, because a pointer will not become NULL, if someone deletes the pointed object).

The best solution here (provided by container with pointers) would be to use some smart pointers - some with reference count, for example; they will guarantee you that the object will exist and that the pointer is valid. And in case that the object, pointed by the smart pointer, is destroyed, you can check it for NULL.

Returning a vector of references from a Class

In order to store references in a std::vector you'll have to use an std::reference_wrapper.

#include <functional>
#include <vector>

int main( )
{
std::vector<int> ints{ 1, 2, 3, 4, 5 };
std::vector<std::reference_wrapper<int>> references;

for( auto& i : ints )
references.push_back( i );

auto& ref{ references[ 1 ].get( ) };
ref += 20;

// Prints 22.
std::cout << ints[ 1 ] << '\n';
}

Here is another example which more closely resembles what you're trying to do (I also included what headers you need).

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

struct ValuationFunction
{
ValuationFunction( int prop )
: property{ prop } { }
int property;
};


// Converts the vector of pointers to a vector
// of references.
static std::vector<std::reference_wrapper<ValuationFunction>>
ToReference( const std::vector<std::shared_ptr<ValuationFunction>>& pointers )
{
std::vector<std::reference_wrapper<ValuationFunction>> references;
references.reserve( pointers.size( ) );

for ( auto& pointer : pointers )
references.emplace_back( *pointer );

return references;
}

int main( )
{
std::vector<std::shared_ptr<ValuationFunction>> pointers
{
std::make_shared<ValuationFunction>( 1 ),
std::make_shared<ValuationFunction>( 2 ),
std::make_shared<ValuationFunction>( 3 ),
std::make_shared<ValuationFunction>( 4 )
};

auto references{ ToReference( pointers ) };

// Use 'std::reference_wrapper.get( )' to get
// access to the contained reference.
for ( auto& ref : references )
std::cout << "Before: " << ref.get( ).property << '\n';

// Change the property value of each 'ValuationFunction'
// in the original list.
for ( auto& pointer : pointers )
pointer->property += 10;

// Re-print the property value of each reference and see
// how they have all increased by 10.
for ( auto& ref : references )
std::cout << "After: " << ref.get( ).property << '\n';
}

vector of references and ability to resize

error: no matching function for call to ‘std::reference_wrapper::reference_wrapper()’

That says wrapper's default constructor was not found. It does not have one because references must always be initialized.

std::vector<T>::resize uses this default constructor to initialize new elements. Think about it, what should this print?

std::vector<std::reference_wrapper<A>> vec;
vec.resize(100); # There now must be 100 valid elements.
std::cout << vec[50].get().getval();

If you want default semantics, use pointers.

If you call resize as an optimization, just use reserve+push_back.

Vector Of References To Unique Ptr Invalid Read

You're taking a reference to an element stored in the vector. This is fine as long as the vector doesn't resize itself, since the storage location of the element will not change. But, as soon as the vec vector needs to resize to hold one more element than its capacity, it moves all its current elements over to a new backing memory block, and the references you took now refer to freed memory (i.e. garbage).

creating a vector with references to some of the elements of another vector

I think the safest thing would be to have a second vector that holds indexes into the first vector:

using std::vector;
vector<A> main;
vector<vector<A>::size_type> secondary;

main.push_back(...);
secondary.push_back(main.size() - 1); // add the index of the last item

Now, to look up an item you take the value in secondary and use that to index into main:

main[secondary[...]];

The reason I recommend this instead of just having secondary store a direct pointer is that every time you add to vector it may need to resize the vector which can invalidate any existing pointers:

using std::vector;
vector<A> vec;
vec.push_back(A());

// get a pointer to the item you just added
A *p0 = &vec[0];

// add another item
a.push_back(A());

// because a push_back() can cause the vector to resize, p0 may no
// longer point to valid memory and cannot safely be dereferenced

Accessing element from reference to vector of pointers

That objects is a reference has no direct bearing on the question -- you use a reference in exactly the same way you use a simple identifier for the same object.

Thus, you can select an individual element of the vector by any of the normal means, such as by index, by iterator, or whatever. The result is of the vector's element type, which is Object *:

Object *o = objects[42];

You can access the object to which o points via the dereference operator (unary *). Alternatively, you can use the indirect access operator (->) to access its methods and fields:

(*o).do_something();
int i = o->an_int;


Related Topics



Leave a reply



Submit