【问题标题】:Unordered_set: remove with moveUnordered_set:移动删除
【发布时间】:2014-01-31 19:16:57
【问题描述】:

在 c++11 中,std::unordered_set 容器提供了插入重载和新函数 emplace,以便它可以与不可复制构造的键一起使用,例如 std::unique_ptr。

当您想删除其中一个键时会发生什么? auto temp = std::move(*some_iterator) 有效吗?是否有一些函数可以让我们同时擦除一个元素并将其移动到一个临时对象中?

编辑:我尽量保持简短、甜美和简单,但要更清楚:

  • 是否有一个迭代器适配器(可能是 move_iterator?)可以让我从容器中移动一个元素并删除该迭代器?
  • 如果不是,为什么不呢?未来的 c++ 不应该包含这种接口吗?

情况似乎是不可能的:你不能在删除之前使密钥失效,删除后你也无法访问它。

【问题讨论】:

  • 在 C++11 中 std::unordered_container 不存在。
  • @Jefffrey 错字,已修复
  • 我建议将您的数据结构从unordered_set<unique_ptr<T>> 切换到unordered_map<T*, unique_ptr<T>>,这样您就可以在擦除之前检索unique_ptrauto temp = std::move(some_iterator->second);
  • @Casey 这只是一个例子(选择是常见的) - 我想编写可以使用非复制构造对象的通用代码。 api好像是不对称的。

标签: c++ c++11 move-semantics


【解决方案1】:

对于std::unordered_set<T>,成员emplace() 使得对象不必是可移动的:您可以将emplace() 不可移动的对象放入容器中。

回答这个问题:你不能在std::unordered_set<T> 中使用std::move() 元素,因为这些元素都是const 并且const 对象无法移动。当您从std::unordered_set<std::unique_ptr<T>>erase()std::unique_ptr<T> 时,指向的对象将是deleted:没有接口允许您在erase()ing 元素时恢复指针。也没有splice() 成员可以将元素粘贴到另一个容器中,在那里它可以被忽略,直到需要摆脱它为止。

【讨论】:

  • 是的 - 这就是我发现自己的情况,我想知道是否有办法解决。似乎您无法在删除元素之前使其无效,并且您无法删除元素而不删除它。我问这个问题是因为它看起来像一个 catch-22 类型的情况。除非你有一个标准的摘录说这是不可能的,否则答案不是很有用,抱歉。
  • @user3125280:根本没有catch-22的情况!如果您将std::unique_ptr<T> 粘贴到std::unordered_set<std::unique_ptr<T>> 中,您将无法将其从容器中移除,同时保持其存活。那是。你想要什么标准的摘录? std::unordered_set<T> 仅提供对 T const& 的访问权限,并且无法将其恢复为非 const 对象。换句话说:你想要的是做不到的。不过,您可以使用std::unordered_map<T*, std::unique_ptr<T>> 做类似您想做的事情:您可以将值移到那里。
  • 那么它们在重新散列期间是如何移动的?将赋值转移到 const?如果我能抹去它,我应该能够以某种方式摆脱它..
  • @user3125280:在内部,无序容器保持节点嵌入实际对象(您可以看出,因为指向元素的指针不会通过重新散列而失效;只有迭代器会失效)。也就是说,当一些指针被更新时,实际对象保持不变。这就是我评论splice() 的原因:内部有某种形式的列表,我可以想象有一种方法可以将节点移动到单独的容器中,但没有。
  • 但如果它被删除(甚至在内部),那么就会有一个可变副本——无论如何我都无法通过任何界面访问它?
【解决方案2】:

我的解决方案是将元素放在std::shared_ptr 中。这样您就可以复制它们,然后最终将元素移出shared_ptr

在编程方面,这样做的代价是您必须自己定义散列函数。

假设您将 std::string 放入集合中:

struct MyHasher
{
    size_t operator()(const std::shared_ptr<std::string>& element) const
    {
        return std::hash<std::string>()(*element);
    }
};

然后你像这样制作unordered_set

std::unordered_set<std::string,MyHasher> the_set;

现在,您可以随意移动元素。

【讨论】:

  • 问题是 api 支持插入不可复制构造的密钥,但不支持删除它们。这是移动语义的限制。移动与析构函数是分开的,因此不可能从一个常量对象移动而不破坏它两次(在这种情况下,在同一个指针上调用两次删除)。一般来说,要么api需要访问可变键(坏主意),要么对象需要组合移动/析构函数(坏主意),或者程序员只使用可复制对象(好主意,但单例不应该是可复制的)所以在实践中这不是问题。
  • 如果出于某种原因你对这种事情感兴趣,我在另一个问题here
  • @user3125280 实际上我遇到了这个问题,但shared_ptr 对我的情况来说是一个很好的解决方案。感谢您的链接:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-05
  • 1970-01-01
  • 2017-05-19
  • 1970-01-01
  • 2013-07-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多