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

元气小坏坏 提交于 2021-01-24 08:04:57

问题


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?


回答1:


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



回答2:


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.




回答3:


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)



来源:https://stackoverflow.com/questions/31309708/access-elements-of-vectorstdunique-ptrt-using-an-iterator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!