使用std :: unordered_set的std :: unique_ptr

时间:2013-07-25 06:56:58

标签: c++ c++11 stl unique-ptr unordered-set

假设我有一组unique_ptr:

std::unordered_set <std::unique_ptr <MyClass>> my_set;

我不确定检查集合中是否存在给定指针的安全方法是什么。正常的方法可能是调用my_set.find (),但我作为参数传递什么?

我从外面得到的只是一个原始指针。所以我必须从指针创建另一个unique_ptr,将其传递给find(),然后将release()传递给指针,否则对象将被破坏(两次)。当然,这个过程可以在一个函数中完成,因此调用者可以传递原始指针并进行转换。

这种方法安全吗?有没有更好的方法来处理一组unique_ptr?

4 个答案:

答案 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));

Live example.

答案 1 :(得分:11)

请注意,在标准容器上执行异构查找的能力是一些提案的主题。

http://cplusplus.github.io/LWG/lwg-proposal-status.html列出

  • N3465为TR2(Rev 2)的关联容器添加异构比较查找[使用N3573处理]
  • N2882 id。
  • N3573无序容器的异质扩展[使用N3465处理]

特别是后者看起来会覆盖你的用例。

目前,这里有一个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应该相当便宜,并且改进了 此解决方案的位置可能会抵消额外的运行时 否则就会产生的成本。