Лучший способ удалить std::unique_ptr из вектора с необработанным указателем?

Итак, у меня есть такой вектор:

std::vector<std::unique_ptr<SomeClass>> myVector;

Затем у меня есть другой вектор, который содержит необработанные указатели SomeClass:

std::vector<SomeClass*> myOtherVector;

Если внутри myOtherVector есть элемент, он также будет внутри myVector, поэтому я хочу просмотреть каждый элемент в myOtherVector и удалить тот же элемент из myVector. Затем очистите вектор. Вот что я придумал:

for(size_t i = 0; i < myOtherVector.size(); i++)
{
    myVector.erase(std::remove(myVector.begin(), myVector.end(), myOtherVector[i]), myVector.end());
}
myOtherVector.clear();

Это приводит к ошибке времени компиляции, потому что myVector содержит уникальные указатели, но я даю функции remove() необработанный указатель. Здесь мне нужна помощь, потому что я не знаю, как правильно решить эту проблему. Я изменил строку на:

myVector.erase(std::remove(myVector.begin(), myVector.end(), std::unique_ptr<SomeClass>(myOtherVector[i])), myVector.end());

Во-первых, это неверно, потому что теперь у меня есть два std::unique_ptr, ссылающихся на один и тот же объект. Элемент внутри myVector содержит ссылку, а конструкция уникального указателя в приведенной выше строке является еще одной ссылкой. И я даже не знаю, является ли создание нового указателя для получения того же типа концептуально правильным способом сделать это. Итак, я изменил уникальные указатели на общие указатели:

std::vector<std::shared_ptr<SomeClass>> myVector;
std::vector<SomeClass*> myOtherVector;

for(size_t i = 0; i < myOtherVector.size(); i++)
{
    myVector.erase(std::remove(myVector.begin(), myVector.end(), std::shared_ptr<SomeClass>(myOtherVector[i])), myVector.end());
}
myOtherVector.clear();

Когда я запустил приложение, строка myVector.erase() привела к ошибке времени выполнения, в которой говорилось, что «ApplicationName.exe вызвал точку останова». после нажатия кнопки «Продолжить» я получил ошибку утверждения отладки.

Так что, очевидно, я делаю что-то не так, но я не знаю, что. Каков правильный способ стереть интеллектуальный указатель из вектора с помощью необработанного указателя?


person ProgrammerGuy123    schedule 28.02.2013    source источник
comment
Рассматривали ли вы возможность упростить задачу who, просто отказавшись от поддержки вектора необработанных указателей?   -  person Ed S.    schedule 28.02.2013
comment
std::unique_ptr имеет член get, который возвращает принадлежащий указатель.   -  person James McNellis    schedule 28.02.2013
comment
А, предложение. Есть еще один умный указатель C++11, который называется std::shared_ptr.   -  person Mark Garcia    schedule 28.02.2013
comment
И поправка к моему предыдущему предложению: если вы последуете этому совету, вы можете просто использовать std::vector::clear().   -  person Mark Garcia    schedule 28.02.2013


Ответы (3)


Вот как бы я это сделал. Производительность может быть улучшена, но пока это не станет узким местом для вашего приложения, я бы не беспокоился об этом. Алгоритм прост и ясен.

Он использует remove_if для выборочного удаления из первого контейнера (myVector) всех элементов, указывающих на объекты, на которые указывают элементы второго контейнера (myOtherVector); затем он очищает второй контейнер. Предикат реализован через лямбда-функцию:

#include <vector>
#include <memory>
#include <algorithm>

struct SomeClass { /* ... */ };

int main()
{
    std::vector<std::unique_ptr<SomeClass>> myVector;
    std::vector<SomeClass*> myOtherVector;

    myVector.erase(
        std::remove_if( // Selectively remove elements in the second vector...
            myVector.begin(),
            myVector.end(),
            [&] (std::unique_ptr<SomeClass> const& p)
            {   // This predicate checks whether the element is contained
                // in the second vector of pointers to be removed...
                return std::find(
                    myOtherVector.cbegin(), 
                    myOtherVector.cend(), 
                    p.get()
                    ) != myOtherVector.end();
            }),
        myVector.end()
        );

    myOtherVector.clear();
}
person Andy Prowl    schedule 28.02.2013

std::unique_ptr имеет функцию-член get, которая возвращает принадлежащий указатель.

Рассмотрим следующее:

std::sort(myOtherVector.begin(), myOtherVector.end());

myVector.erase(std::remove_if(myVector.begin(), myVector.end(),
[&](std::unique_ptr<SomeClass> const& p) -> bool
{
    return std::binary_search(myOtherVector.begin(), myOtherVector.end(),
                              p.get());
}));

myOtherVector.clear();    
person James McNellis    schedule 28.02.2013

Если вы не можете упростить свою проблему, как насчет std::set_difference или одного из его родственников (http://www.cplusplus.com/reference/algorithm/set_difference/)?

Вам нужно будет указать функцию сравнения, чтобы получить () необработанный указатель из unique_ptr

person RichardBruce    schedule 04.07.2013