如何通过指针从容器中删除 unique_ptr?

发布于 2024-11-27 12:44:17 字数 254 浏览 1 评论 0原文

使用 unique_ptr 创建对象并赋予容器所有权是没有问题的。如何通过原始指针删除元素?

std::set<std::unique_ptr<MyClass>> mySet;

MyClass *myClass = new MyClass();
mySet.insert(std::unique_ptr<MyClass>(myClass));

// remove myClass from mySet?

Creating an object and giving ownership to a container using a unique_ptr is no problem. How would one remove an element by raw pointer?

std::set<std::unique_ptr<MyClass>> mySet;

MyClass *myClass = new MyClass();
mySet.insert(std::unique_ptr<MyClass>(myClass));

// remove myClass from mySet?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

梦言归人 2024-12-04 12:44:17

您需要找到与 myClass 元素对应的迭代器,然后将该迭代器传递给 mySet.erase()。可以使用 std::find_if 算法和自定义 Predicate 函子来找到迭代器,该函子了解如何取消引用 unique_ptr 并将其与原始值进行比较指针myClass

您不能使用重载的 size_t set::erase ( const key_type& x );,因为在以下位置找不到原始指针(即使包装在临时 unique_ptr 中) mySet

You will need to find the iterator corresponding to the myClass element and then pass that iterator to mySet.erase(). The iterator may be found using the std::find_if algorithm with a custom Predicate functor that understands how to dereference unique_ptr and compare it to the raw pointer myClass.

You can not use the overloaded size_t set::erase ( const key_type& x ); since the raw pointer (even if wrapped in a temporary unique_ptr) will not be found in mySet.

信仰 2024-12-04 12:44:17

没有我想要的那么漂亮。但以下内容可以完成这项工作:

#include <memory>
#include <set>
#include <iostream>

struct do_nothing
{
    void operator()(const void*) const {}
};

struct MyClass
{
    MyClass() {std::cout << "MyClass()\n";}
    MyClass(const MyClass&) {std::cout << "MyClass(const MyClass&)\n";}
    ~MyClass() {std::cout << "~MyClass()\n";}
};

int main()
{
    std::set<std::unique_ptr<MyClass>> mySet;

    MyClass *myClass = new MyClass();
    mySet.insert(std::unique_ptr<MyClass>(myClass));

    // remove myClass from mySet?
    std::set<std::unique_ptr<MyClass>>::iterator i =
        lower_bound(mySet.begin(), mySet.end(),
                    std::unique_ptr<MyClass, do_nothing>(myClass));
    if (i != mySet.end() && *i == std::unique_ptr<MyClass, do_nothing>(myClass))
        mySet.erase(i);
}

Not as pretty as I would've liked. But the following does the job:

#include <memory>
#include <set>
#include <iostream>

struct do_nothing
{
    void operator()(const void*) const {}
};

struct MyClass
{
    MyClass() {std::cout << "MyClass()\n";}
    MyClass(const MyClass&) {std::cout << "MyClass(const MyClass&)\n";}
    ~MyClass() {std::cout << "~MyClass()\n";}
};

int main()
{
    std::set<std::unique_ptr<MyClass>> mySet;

    MyClass *myClass = new MyClass();
    mySet.insert(std::unique_ptr<MyClass>(myClass));

    // remove myClass from mySet?
    std::set<std::unique_ptr<MyClass>>::iterator i =
        lower_bound(mySet.begin(), mySet.end(),
                    std::unique_ptr<MyClass, do_nothing>(myClass));
    if (i != mySet.end() && *i == std::unique_ptr<MyClass, do_nothing>(myClass))
        mySet.erase(i);
}
玉环 2024-12-04 12:44:17

看来我能够使用带有 lower_bound 的自定义谓词来检索迭代器。由于 std::set 是一个有序容器,因此 lower_bound 应该以对数方式执行。

std::set<std::unique_ptr<MyClass>>::iterator i =
    std::lower_bound(mySet.begin(), mySet.end(), myClass, MyPredicate<MyClass>());

template<class Type>
struct MyPredicate
{
    bool operator()(const std::unique_ptr<Type>& left, const Type* right) const
    {
        return left.get() < right;
    }
}

It seems i am able to retrieve an iterator using a custom Predicate with lower_bound. Since std::set is an ordered container, lower_bound should perform logarithmically.

std::set<std::unique_ptr<MyClass>>::iterator i =
    std::lower_bound(mySet.begin(), mySet.end(), myClass, MyPredicate<MyClass>());

template<class Type>
struct MyPredicate
{
    bool operator()(const std::unique_ptr<Type>& left, const Type* right) const
    {
        return left.get() < right;
    }
}
金兰素衣 2024-12-04 12:44:17

仍然不是最好的解决方案,但目前我选择:

PointerMap<MyFoo>::Type myFoos;

MyFoo * myFoo = new MyFoo();
myFoos.insert(PointerMap<MyFoo>::Item(myFoo));

标题是:

#include <map>
#include <memory>
#include <utility>

template<typename T>
struct PointerMap
{
    typedef std::map<T *, std::unique_ptr<T>> Type;

    struct Item : std::pair<T *, std::unique_ptr<T>>
    {
        Item(T * pointer)
            : std::pair<T *, std::unique_ptr<T>>(pointer, std::unique_ptr<T>(pointer))
        {
        }
    };
};

Still not the best solution but for the moment i go with:

PointerMap<MyFoo>::Type myFoos;

MyFoo * myFoo = new MyFoo();
myFoos.insert(PointerMap<MyFoo>::Item(myFoo));

The header is:

#include <map>
#include <memory>
#include <utility>

template<typename T>
struct PointerMap
{
    typedef std::map<T *, std::unique_ptr<T>> Type;

    struct Item : std::pair<T *, std::unique_ptr<T>>
    {
        Item(T * pointer)
            : std::pair<T *, std::unique_ptr<T>>(pointer, std::unique_ptr<T>(pointer))
        {
        }
    };
};
因为看清所以看轻 2024-12-04 12:44:17

您可能喜欢这里的答案:有效擦除a unique_ptr from an unordered_set

这适用于 C++14,但我认为也适用于 C++11。

它并不漂亮,但效率很高——不扫描容器,而是使用适当的基于哈希的查找。

You might like the answer over here: Efficiently erase a unique_ptr from an unordered_set

That's for C++14, but I think applies to C++11 as well.

It is not pretty, but does the efficient thing — no scanning the container, but using proper hash-based lookup.

征棹 2024-12-04 12:44:17

异常安全的解决方案,具有基于哈希的高效查找(因此消除了 std::find_if 或 std::lower_bound 的线性时间复杂度)并且没有透明度限制(这是我对类似问题的回答的副本):

/*
 * Behaves like an std::unique_ptr that
 * does not delete the pointer on destruction
*/
template<typename T>
class bad_ptr
{
private:
    std::unique_ptr<T> m_ptr;

public:
    // construct from a pointer
    bad_ptr(T* ptr) : m_ptr{ptr} { }

    // convert to an std::unique_ptr&
    operator const std::unique_ptr<T>&() {
        return m_ptr;
    }

    // release the pointer without deleting it
    ~bad_ptr() {
        m_ptr.release();
    }
};

用法:

struct test_struct {
    int a;
    int b;
};

int main()
{
    std::unordered_set<std::unique_ptr<test_struct>> set;
    auto raw_ptr = set.insert(std::make_unique<test_struct>(1, 2)).first->get();

    // error
    // set.erase(raw_ptr);

    // works
    set.erase(bad_ptr{raw_ptr});
}

以便您得到

mySet.erase(bad_ptr{myClass});

Exception-safe solution with efficient hash-based lookup (so the linear time complexity of std::find_if or std::lower_bound is eliminated) and no transparency restrictions (this is a copy of my answer to a similar question):

/*
 * Behaves like an std::unique_ptr that
 * does not delete the pointer on destruction
*/
template<typename T>
class bad_ptr
{
private:
    std::unique_ptr<T> m_ptr;

public:
    // construct from a pointer
    bad_ptr(T* ptr) : m_ptr{ptr} { }

    // convert to an std::unique_ptr&
    operator const std::unique_ptr<T>&() {
        return m_ptr;
    }

    // release the pointer without deleting it
    ~bad_ptr() {
        m_ptr.release();
    }
};

Usage:

struct test_struct {
    int a;
    int b;
};

int main()
{
    std::unordered_set<std::unique_ptr<test_struct>> set;
    auto raw_ptr = set.insert(std::make_unique<test_struct>(1, 2)).first->get();

    // error
    // set.erase(raw_ptr);

    // works
    set.erase(bad_ptr{raw_ptr});
}

So that you get

mySet.erase(bad_ptr{myClass});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文