Подтвердить что ты не робот

С++ unique_ptr и карта

Я пытаюсь использовать С++ 0x unique_ptr class внутри map, например:

// compile with `g++ main.cpp -std=gnu++0x`

#include <string.h>    
#include <map>
#include <memory>

using namespace std;

struct Foo {
    char *str;    
    Foo(char const *str_): str(strdup(str_)) {}
};

int main(void) {
    typedef std::map<int, unique_ptr<Foo>> Bar;
    Bar bar;
    auto a = bar.insert(Bar::value_type(1, new Foo("one")));
    return 0;
}

Однако GCC дает мне следующую ошибку (сокращенный, я думаю, что это соответствующая часть, пожалуйста, протестируйте свой собственный компилятор С++):

main.cpp:19:   instantiated from here
/usr/include/c++/4.4/bits/unique_ptr.h:214: error: deleted function ‘std::unique_ptr::unique_ptr(const std::unique_ptr&) [with _Tp = Foo, _Tp_Deleter = std::default_delete]’
/usr/include/c++/4.4/bits/stl_pair.h:68: error: used here

Я действительно не уверен, что я сделал неправильно, это работает на MSVC. Я нашел очень похожие вопросы, которые кажутся похожими, однако их решения не работают для меня.

[email protected]:/media/data/src/c++0x-test$ gcc --version
gcc-4.4.real (Ubuntu 4.4.3-4ubuntu5) 4.4.3
4b9b3361

Ответ 1

Я не считаю возможным правильно использовать unique_ptr в map::insert. Здесь ошибка GCC для него:

Ошибка 44436 - [С++ 0x] Внедрить insert(&&) и emplace* в ассоциативные и неупорядоченные контейнеры

Похоже, что это может быть исправлено для GCC 4.6, но он не будет строить из SVN на ваниле Ubuntu 10.04.1 для подтверждения.

Update0

Это не работает как GCC svn r165422 (4.6.0).

Ответ 2

Во-первых: ваш класс Foo действительно очень плохая аппроксимация std::string. Я предсказываю много утечек памяти. (Найдите "правило из трех".)

Второе: указатель неявно конвертируется в объект unique_ptr (по соображениям безопасности). Но шаблонный конструктор пары требует, чтобы аргументы были неявно конвертируемыми в соответствующие типы значений. GCC, похоже, допускает это, но это ошибка. Вы должны создать объект unique_ptr вручную. К сожалению, проект С++ 0x не имеет следующего, очень полезного шаблона функции, который облегчает создание временных объектов unique_ptr. Это также безопасно:

template<class T, class...Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    std::unique_ptr<T> ret (new T(std::forward<Args>(args)...));
    return ret;
}

Кроме того, можно использовать новую функцию emplace вместо insert:

auto a = bar.emplace(1,make_unique<Foo>("one"));

emplace переводит оба аргумента в соответствующий конструктор пары.

Фактическая проблема, с которой вы стали свидетелем, заключается в том, что конструктор пары попытался скопировать уникальный_ptr, хотя такой объект только "движимый". Кажется, что поддержка GCC С++ 0x еще недостаточно хороша для правильной обработки этой ситуации. Из того, что я могу сказать, моя линия сверху должна работать в соответствии с действующим стандартным проектом (N3126).

Изменить: Я просто попробовал следующее в качестве обходного пути с использованием GCC 4.5.1 в экспериментальном режиме С++ 0x:

map<int,unique_ptr<int> > themap;
themap[42].reset(new int(1729));

Это также должно работать в предстоящем стандарте, но GCC отвергает его. Похоже, вам нужно подождать, пока GCC поддержит unique_ptr в картах и ​​мультифайлах.

Ответ 3

Я только что играл с unique_ptr в gcc 4.6.0. Вот какой (очень уродливый) код, который работает правильно:

std::map<int, std::unique_ptr<int> > table;
table.insert(std::pair<int, std::unique_ptr<int>>(15, std::unique_ptr<int>(new int(42))));
table[2] = std::move(table[15]);
for (auto& i : table)
{
    if (i.second.get())
        printf("%d\n", *i.second);
    else
    {
        printf("empty\n");
        i.second.reset(new int(12));
    }
}
printf("%d\n", *table[15]);

Выход равен 42, пустой, 12 - так, видимо, он работает. Насколько я могу судить, единственная "проблема" в том, что ужасно выглядящая команда вставки. "emplace" не реализован в gcc 4.6.0

Ответ 4

Попробуйте использовать способ отображения emplace.