【问题标题】:Move std::vector<std::unique_ptr<T>> to std::vector<std::shared_ptr<T>>移动 std::vector<std::unique_ptr<T>> 到 std::vector<std::shared_ptr<T>>
【发布时间】:2018-02-02 11:49:40
【问题描述】:

有时我们有一个工厂,它产生一个 std::unique_ptr 的向量,稍后我们想在类/线程/你命名它之间共享这些指针。所以最好改用 std::shared_ptr 。当然有一种方法可以将 std::uniqe_ptr 转换为 std::shared_ptr

std::shared_ptr<int> sharedV;
std::unique_ptr<int> uniqueV(new int(2));

sharedV = std::move(uniqueV);

那么有什么直接的方法可以用 std 集合做这样的事情吗?

【问题讨论】:

  • 所以最好改用 std::shared_ptr 这些东西需要参与所有权吗?如果没有,您可以通过引用传递向量。
  • @NathanOliver 对于我们希望共享这些指针所有权的某些特定情况是理想的。不要过分热心:)

标签: c++ c++11 stl c++14


【解决方案1】:

您可以使用来自&lt;algorithm&gt;std::move 来移动范围。它的行为很像std::copy,但会移动。以下示例会将所有unique_ptruniqueV 移动到sharedVuniqueV 的元素在示例末尾都将是 nullptr

#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>

int main()
{

    std::vector<std::shared_ptr<int>> sharedV;
    std::vector<std::unique_ptr<int>> uniqueV;

    uniqueV.emplace_back(std::make_unique<int>(42));

    std::move(uniqueV.begin(), uniqueV.end(), std::back_inserter(sharedV));    

    return 0;
}

【讨论】:

  • 会建议[令人沮丧地]更冗长但也更有效sharedV.insert(sharedV.end(), std::make_move_iterator(uniqueV.begin()), std::make_move_iterator(uniqueV.end()))
  • @Barry 我不知道std::make_move_iterator。很有意思。将该解决方案附加到我的答案中我会感到不舒服,我觉得它应该是一个独特的答案。
  • 好吧,如果你坚持:)
【解决方案2】:

要在François Andrieux's answer 之上添加,std::vector 有一个范围 insert() 成员函数。仅将迭代器传递给 unique_ptr 的向量是行不通的,但是有一种方法可以将这些迭代器的解引用从左值转换为 x 值:std::move_iterator 及其相应的工厂函数:std::make_move_iterator

sharedV.insert(sharedV.end(),
    std::make_move_iterator(uniqueV.begin()),
    std::make_move_iterator(uniqueV.end()));

这可能比使用std::back_inserter 更有效的原因是insert() 会预先知道结果大小,所以最多只需要完成一次分配,然后不需要实际插入做尺寸检查。

这写起来太尴尬了,我建议这个命名为extend()的基于范围的重载:

template <class T, class Range>
void extend(std::vector<T>& dst, Range&& range) {
    using std::begin; using std::end;
    if constexpr (std::is_lvalue_reference<Range>{}) {
        dst.insert(dst.end(), begin(range), end(range));
    } else {
        dst.insert(dst.end(),
            std::move_iterator(begin(range)), std::move_iterator(end(range)));
    }
}

这是 C++17,但在 C++14 中很容易实现。只是需要更多的打字。然后你会写:

extend(sharedV, std::move(uniqueV));

【讨论】:

  • 我发现首先调整目标向量的大小然后再调整移动算法(或 for 循环)更具可读性。在这种情况下,这应该同样有效。
  • 实现者可以使用back_inserter进行同样的优化,对吧?
  • @GManNickG 怎么样?通过添加采用输出迭代器的每个算法的重载?我猜有可能,但似乎不是特别实用。
  • @Barry:我在想如果迭代器是随机访问迭代器,并且输出迭代器是一个back_inserter,实现可以额外检查back_inserter中的容器是否可以reserve,如果是,调用reserve(std::distance(begin, end))。然后像往常一样传递。它应该在编译时都是可行的。不过,看看我最喜欢的实现,它似乎并没有这样做。
  • 尽管再三考虑,move 的允许操作可能完全禁止这种 w.r.t 异常。容量改变可能是不可取的。
猜你喜欢
  • 2019-10-20
  • 2019-11-07
  • 1970-01-01
  • 1970-01-01
  • 2015-10-11
  • 2012-06-21
  • 1970-01-01
  • 1970-01-01
  • 2017-09-01
相关资源
最近更新 更多