【问题标题】:Is it possible to extract an element from a set without copying it?是否可以从集合中提取元素而不复制它?
【发布时间】:2018-04-13 21:55:10
【问题描述】:

这类似于Moving elements out of an associative container,但不完全相同。考虑以下函数 pop 从容器中移除元素并返回它:

#include <utility>
#include <vector>
#include <set>
#include <memory>
#include <iostream>

using namespace std;

template<typename T>
typename T::value_type pop(T &collection)
{
    auto it = collection.begin();
    auto value = move(*it);
    collection.erase(it);
    return move(value);
}

int main()
{
    vector<unique_ptr<int>> v;
    v.push_back(make_unique<int>(1));
    v.push_back(make_unique<int>(2));
    cout << "pop(v): " << *pop(v) << endl;  // prints "pop(v): 1"
    set<unique_ptr<int>> s;
    s.insert(make_unique<int>(1));
    s.insert(make_unique<int>(2));
    // cout << "pop(s): " << *pop(s) << endl;  // This does not compile
    return 0;
}

显然,注释行无法编译,因为 setunordered_set 等关联容器的迭代器仅提供 const 对元素的访问(我确实理解这样做的原因)和 @987654327 @ 无法复制。但是,正如您所知,在这种情况下移动值是“合法的”,因为我实际上是从容器中删除它(因此它不需要是不可修改的),所以问题是,有没有办法实现这个以安全、合法的方式?或者从集合中提取元素是否必然涉及副本?我想我可以const_cast 并且它可能会工作,但据我所知,那将是 UB。这对于重型类型来说很麻烦,但对于不可复制的类型来说更是如此,一旦它们被插入到集合中就会永远被“监禁”。

【问题讨论】:

  • 问题在于容器是如何实现的。将元素移出集合会在其内部数据结构中留下一个“洞”。由于迭代器对其关联容器一无所知,因此迭代器无法要求集合擦除已移动的元素并修补漏洞;只会有一个内部 "set::node" 包含无效数据。
  • 这个例子不能按原样工作,这让我很困扰。我明白为什么它没有,但正如你所描述的,从使用的角度来看,没有实际的逻辑原因。因此,这种限制是一个巨大的抽象泄漏,坦率地说,引入新的节点处理功能并不能解决这个问题(从完美主义者的角度来看)。这就是 C++ 做得不好的地方。

标签: c++ set move-semantics


【解决方案1】:

C++17 为关联容器引入了node_handles。它们允许从关联容器中删除元素而不复制它们。特别是,您希望的行为可以通过extract 函数实现:

#include <utility>
#include <vector>
#include <set>
#include <memory>
#include <iostream>

using namespace std;

template<typename T>
typename T::value_type pop(T &collection)
{
    auto node = collection.extract(begin(collection));
    return move(node.value());
}

int main()
{
    set<unique_ptr<int>> s;
    s.insert(make_unique<int>(1));
    s.insert(make_unique<int>(2));
    cout << "pop(s): " << *pop(s) << endl;
    return 0;
}

【讨论】:

    猜你喜欢
    • 2021-09-21
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-19
    • 2010-09-08
    • 2018-10-02
    • 1970-01-01
    相关资源
    最近更新 更多