假设我有一组unique_ptr:
std::unordered_set <std::unique_ptr <MyClass>> my_set;
我不确定检查集合中是否存在给定指针的安全方法是什么。正常的方法可能是调用my_set.find ()
,但我作为参数传递什么?
我从外面得到的只是一个原始指针。所以我必须从指针创建另一个unique_ptr,将其传递给find()
,然后将release()
传递给指针,否则对象将被破坏(两次)。当然,这个过程可以在一个函数中完成,因此调用者可以传递原始指针并进行转换。
这种方法安全吗?有没有更好的方法来处理一组unique_ptr?
答案 0 :(得分:22)
您还可以使用可选择不执行任何操作的删除程序。
template<class T>
struct maybe_deleter{
bool _delete;
explicit maybe_deleter(bool doit = true) : _delete(doit){}
void operator()(T* p) const{
if(_delete) delete p;
}
};
template<class T>
using set_unique_ptr = std::unique_ptr<T, maybe_deleter<T>>;
template<class T>
set_unique_ptr<T> make_find_ptr(T* raw){
return set_unique_ptr<T>(raw, maybe_deleter<T>(false));
}
// ...
int* raw = new int(42);
std::unordered_set<set_unique_ptr<int>> myset;
myset.insert(set_unique_ptr<int>(raw));
auto it = myset.find(make_find_ptr(raw));
答案 1 :(得分:11)
请注意,在标准容器上执行异构查找的能力是一些提案的主题。
http://cplusplus.github.io/LWG/lwg-proposal-status.html列出
特别是后者看起来会覆盖你的用例。
目前,这里有一个IMO不是很漂亮,但工作替代解决方法(O(n)):
#include <iterator>
#include <iostream>
#include <algorithm>
#include <unordered_set>
#include <memory>
#include <cassert>
struct MyClass {};
template <typename T>
struct RawEqualTo
{
RawEqualTo(T const* raw) : raw(raw) {}
bool operator()(T const* p) const
{ return raw == p; }
bool operator()(std::unique_ptr<T> const& up) const
{ return raw == up.get(); }
private:
T const* raw;
};
using namespace std;
int main()
{
std::unordered_set <std::unique_ptr <MyClass>> my_set;
my_set.insert(std::unique_ptr<MyClass>(new MyClass));
my_set.insert(std::unique_ptr<MyClass>(new MyClass));
auto raw = my_set.begin()->get();
bool found = end(my_set) != std::find_if(begin(my_set), end(my_set), RawEqualTo<MyClass>(raw));
assert(found);
raw = new MyClass;
found = end(my_set) != std::find_if(begin(my_set), end(my_set), RawEqualTo<MyClass>(raw));
assert(!found);
delete raw;
}
警告当然,效率也非常低。
答案 2 :(得分:8)
您可以使用std::map<MyClass*, std::unique_ptr<MyClass>>
代替集合。然后你可以添加这样的元素:
std::unique_ptr<MyClass> instance(new MyClass);
map.emplace(instance.get(), std::move(instance));
答案 3 :(得分:6)
如果目标是查找的恒定时间,我不这么认为
有一个解决方案。
std::unordered_set<std::unique_ptr<MyClass>>::find
需要一个
std::unique_ptr<MyClass>
作为论据。你必须要改变
容器,或更改包含的类型。
一种可能性是将std::unique_ptr
替换为
std::shared_ptr
,并更改代码的其余部分以便全部
MyClass
创建后立即放入shared_ptr,
并且只能通过共享指针进行操作。从逻辑上讲,
无论如何,这可能更加连贯:unique_ptr
暗示(通过它的名字,以及它的语义)那里
不是对象的其他指针。另一方面,你可以
如果是,则无法使用shared_ptr MyClass
有指向
其他MyClass
,可能会构建一个循环。
否则,如果您可以接受O(lg n)访问,而不是
持续访问(差异一般不会成为
在表格相当大之前会很明显,你可以使用
std::vector<MyClass>
,使用std::lower_bound
来保留它
排序。与std::unordered_set<>::find
不同,std::lower_bound
不是否要求目标值具有相同的类型
value_type
序列;你所要做的就是确保
通过提供Compare
对象,它们具有可比性
沿着这条线:
class MyClassPtrCompare
{
std::less<MyClass const*> cmp;
public:
bool operator()( std::unique_ptr<MyClass> const& lhs,
std::unique_ptr<MyClass> const& rhs ) const
{
return cmp( lhs.get(), rhs.get() );
}
bool operator()( MyClass const* lhs,
std::unique_ptr<MyClass> const& rhs ) const
{
return cmp( lhs, rhs.get() );
}
bool operator()( std::unique_ptr<MyClass> const& lhs,
MyClass const* rhs ) const
{
return cmp( lhs.get(), rhs );
}
bool operator()( MyClass const* lhs,
MyClass const* rhs ) const
{
return cmp( lhs, rhs );
}
};
插入可能涉及许多动作,但移动
std::unique_ptr
应该相当便宜,并且改进了
此解决方案的位置可能会抵消额外的运行时
否则就会产生的成本。