Как удалить unique_ptr по указателю из контейнера? - PullRequest
7 голосов
/ 02 августа 2011

Создание объекта и передача прав владения контейнером с использованием unique_ptr не проблема.Как удалить элемент по необработанному указателю?

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

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

// remove myClass from mySet?

Ответы [ 4 ]

3 голосов
/ 02 августа 2011

Не так красиво, как хотелось бы. Но следующее делает работу:

#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);
}
3 голосов
/ 02 августа 2011

Вам потребуется найти итератор, соответствующий элементу myClass, а затем передать этот итератор в mySet.erase(). Итератор может быть найден с использованием алгоритма std::find_if с пользовательским функтором Predicate, который понимает, как разыменовать unique_ptr и сравнивать его с необработанным указателем myClass.

Вы не можете использовать перегруженный size_t set::erase ( const key_type& x );, поскольку необработанный указатель (даже если он помещен во временный unique_ptr) не будет найден в mySet.

1 голос
/ 03 августа 2011

Кажется, я могу получить итератор, используя пользовательский предикат с 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;
    }
}
0 голосов
/ 21 августа 2011

Все еще не лучшее решение, но на данный момент я говорю:

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))
        {
        }
    };
};
...