问题
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 T
s.
When you just want to do stuff with the T
s, 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_ptr
s. 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_ptr
s 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