【发布时间】: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;
}
显然,注释行无法编译,因为 set、unordered_set 等关联容器的迭代器仅提供 const 对元素的访问(我确实理解这样做的原因)和 @987654327 @ 无法复制。但是,正如您所知,在这种情况下移动值是“合法的”,因为我实际上是从容器中删除它(因此它不需要是不可修改的),所以问题是,有没有办法实现这个以安全、合法的方式?或者从集合中提取元素是否必然涉及副本?我想我可以const_cast 并且它可能会工作,但据我所知,那将是 UB。这对于重型类型来说很麻烦,但对于不可复制的类型来说更是如此,一旦它们被插入到集合中就会永远被“监禁”。
【问题讨论】:
-
问题在于容器是如何实现的。将元素移出集合会在其内部数据结构中留下一个“洞”。由于迭代器对其关联容器一无所知,因此迭代器无法要求集合擦除已移动的元素并修补漏洞;只会有一个内部 "set::node" 包含无效数据。
-
这个例子不能按原样工作,这让我很困扰。我明白为什么它没有,但正如你所描述的,从使用的角度来看,没有实际的逻辑原因。因此,这种限制是一个巨大的抽象泄漏,坦率地说,引入新的节点处理功能并不能解决这个问题(从完美主义者的角度来看)。这就是 C++ 做得不好的地方。
标签: c++ set move-semantics