【问题标题】:Access elements of vector<std::unique_ptr<T> > using an iterator?使用迭代器访问 vector<std::unique_ptr<T> > 的元素?
【发布时间】:2015-09-27 08:51:59
【问题描述】:

我有一个类成员变量

vector&lt;std::unique_ptr&lt;T&gt; &gt; v;

和一个成员函数,我想在其中使用vunique_ptr 元素,通过iterator 参数“寻址”。哪一个更好?

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

或者

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

据我所知,第二种方式似乎违反了unique_ptr 的“独特性”。但是std::move() 可以移动*it(参考)吗?顺便说一句,谁真正拥有 unique_ptr 指针、类、成员向量、任何成员函数或其他什么?

【问题讨论】:

  • 您的函数是否只访问T 对象?或者它真的需要迭代器,即必须访问多个Ts 吗?
  • 它确实需要一个迭代器。

标签: c++ vector move unique-ptr


【解决方案1】:

第一个版本获得unique_ptr 目标的所有权,并将向量留为空unique_ptrs。这不太可能是您想要的。第二个版本令人困惑。如果您只想访问由unique_ptrs 管理的对象而不影响所有权,只需使用 de-reerence 运算符:

(*it)->someMethodOfT();

这里,解引用(*it)是解引用迭代器,-&gt;是解引用unique_ptr

记住:unique_ptr 的“唯一性”指的是它的所有权。没有什么可以说许多非所有者不能访问托管对象。但由您决定谁拥有所有权,具体取决于您的应用程序的要求。

【讨论】:

  • 那么unique_ptr 的“所有权”是否意味着“设置”和“获取”唯一一个副本的权限?没有所有权但引用的其他人可以“获取”但不能“设置”?制作另一个副本(例如调用push_back())也是一个“获取”操作?
  • @WanxinGao 不,这意味着谁可以控制它的生命周期。当所有者死亡时,托管对象也会死亡。
  • 好的!所以对于unique_ptr,任何人都有使用权(“get”和“set”),但只有一个人拥有所有权(?)
  • @WanxinGao 是的,没错。所有权可以转让,但只有一个所有者。
【解决方案2】:
p = std::move(*it);

这意味着,p 现在拥有 *it 之前拥有的东西,而 *it 不再拥有它 (*)。

事实上,以后使用*it 可能会导致未定义的行为,因为它已被移出。

*it 后面的unique_ptr 归向量所有。 所以实际上你改变了你的向量。我认为这不是您想要的,所以最好使用参考版本。

[...] 第二种方式似乎违反了“独特性”[...]

在这种情况下,唯一性是指对象的单一所有者。当然,您可能对这个对象的所有者(即指针)有多个引用。


(*) 这也意味着当p 超出范围时,对象将被销毁并释放。 (除非你从 p 某处搬家)

【讨论】:

  • 未定义的行为?我认为使用*it 可能会将向量中的原始unique_ptr 设置为null?
  • 只有取消引用移动的 from 指针才会导致 UB,因为在 move(*it) 之后指针设置为 nullptr。虽然仅仅访问(通过*it)并没有改变任何东西。
【解决方案3】:

post from Herb Sutter 包含回答您问题的知识。

unique_ptr 实际上定义了一个“所有权证书”。此所有权不可复制。当你moveunique_ptr 时,这会有效地破坏存储在`vector 中的证书。

因此,vector&lt;unique_ptr&lt;T&gt;&gt;拥有Ts。

当你只想用Ts 做一些事情时,你应该将你的函数声明为

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

并将解引用委托给另一个函数:

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

并用它来调用你的成员函数:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-07
    • 2018-02-02
    • 2015-10-11
    • 2023-03-16
    • 1970-01-01
    相关资源
    最近更新 更多