vincentvangaogh
vincentvangaogh

Reputation: 372

Access elements of vector<std::unique_ptr<T> > using an iterator?

I have a class member variable as

vector<std::unique_ptr<T> > v;

and a member function where I want to use a unique_ptr element of v "addressed" by an iterator argument. Which one is better?

void mem_fun(vector<std::unique_ptr<T> >::iterator it) {
    std::unique_ptr<T> p;
    p = std::move(*it);
    ...
}

Or

void mem_fun(vector<std::unique_ptr<T> >::iterator it) {
    std::unique_ptr<T>& p = *it;
    ...
}

From what I know, it seems the second way just kind of violates the "uniqueness" of unique_ptr. But can std::move() move *it (a reference)? BTW, who truly owns the unique_ptr pointers, the class, the member vector, any member function, or what else?

Upvotes: 2

Views: 3198

Answers (3)

juanchopanza
juanchopanza

Reputation: 227390

The first version takes ownership of the unique_ptr's target and leaves the vector with empty unique_ptrs. It is unlikely that this is what you want. The second version is confusing. If all you want to do is access the objects managed by the unique_ptrs without affecting ownership, simply use the de-rererence operator(s):

(*it)->someMethodOfT();

Here, the de-reference (*it) is to de-reference the iterator, and the -> is to de-reference the unique_ptr.

Remember: the "uniqueness" of a unique_ptr refers to its ownership. There's nothing to say the managed object can't be accesses by many non-owners. But it is up to you to decide who takes ownership, depending on the requirements of your application.

Upvotes: 1

Daniel Jour
Daniel Jour

Reputation: 16156

p = std::move(*it);

This means, that p is now owning what was owned by *it before, and *it is no longer owning it(*).

In fact, using *it hereafter may cause undefined behaviour, since it has been moved from.

The unique_ptr behind the *it is owned by the vector. So in fact you changed your vector. I think this is not what you intended, so better use the reference version.

[...] it seems the second way just kind of violates the "uniqueness" [...]

Uniqueness in this case means a single owner of the object. You may of course have multiple references to the owner of this object (which is the pointer).


(*) This also means that when p goes out of scope, the object gets destroyed and deallocated. (Unless you moved from p somewhere)

Upvotes: 0

xtofl
xtofl

Reputation: 41509

This post from Herb Sutter contains the knowledge to answer your question.

The unique_ptr actually defines an 'ownership certificate'. This ownership is not copyable. When you move the unique_ptr, this effectively destructs the certificate stored in the `vector.

Therefore, the vector<unique_ptr<T>> owns the Ts.

When you just want to do stuff with the Ts, you should declare your function as

void mem_fun(T& t) { ... }

And delegate the dereferencing to another function:

template<typename It, typename F>
void foreach_deref(It from, It to, F f) {
    std::for_each(from, to, [&](decltype(*from) &pt) {
         f(*pt);
    }
}

And use this to call your member function:

foreach_deref(begin(v), end(v), [&](T& t) { mem_fun(t); });

Upvotes: 1

Related Questions