是否可以从集合中提取元素而不复制它?

时间:2018-04-13 13:39:50

标签: c++ set move-semantics

这类似于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元素的访问(我明白了这个),unique_ptr无法复制。但是,正如你所知,在这种情况下,移动值是&#34; legal&#34;,因为我实际上是从容器中删除它(因此它不需要是不可修改的),所以问题是,是否存在一种以安全,合法的方式实现这一目标的方法吗?或者从集合中提取元素是否必然涉及副本?我想我可以const_cast它可能会起作用,但据我所知,这将是UB。这对于沉重的类型来说很麻烦,但对于不可复制的类型来说更是如此,这些类型将会被判入狱[&34;永远一旦他们插入一套。

1 个答案:

答案 0 :(得分:3)

C ++ 17为关联容器引入了node_handle个。它们允许从关联容器中删除元素而不复制它们。特别是,您可以使用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;
}