web-dev-qa-db-ja.com

C ++ unique_ptrとマップ

C++ 0xunique_ptr classmap 内で次のように使用しようとしています。

// 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では次のエラーが発生します(短縮されました。これは関連する部分だと思います。独自のC++コンパイラでテストしてください)。

 main.cpp:19:ここからインスタンス化
 /usr/include/c++/4.4/bits/unique_ptr.h:214:エラー:削除された関数 '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:エラー:ここで使用

何を間違えたのかよくわかりません。これはMSVCで機能します。私は非常に 類似 の質問を見つけましたが、それらの解決策は私にはうまくいきません。

 matt @ stanley:/ media/data/src/c ++ 0x-test $ gcc --version 
 gcc-4.4.real(Ubuntu 4.4.3-4ubuntu5)4.4.3 
24
Matt Joiner

_unique_ptr_で_map::insert_を正しく使用することはまだ可能ではないと思います。これがGCCのバグです。

Bug 44436- [C++ 0x] insert(&&)および_emplace*_を連想コンテナおよび順序付けされていないコンテナに実装します

GCC 4.6で修正済み のようですが、確認のためにVanilla Ubuntu10.04.1のSVNからビルドされません。

Update0

これは、GCC svn r165422(4.6.0)の時点では機能していません。

6
Matt Joiner

最初に:あなたのクラスFooは本当にstd :: stringの非常に悪い近似です。多くのメモリリークが予測されます。 (「3つのルール」を検索してください。)

2番目:ポインタは(安全上の理由から)unique_ptrオブジェクトに暗黙的に変換できません。ただし、テンプレート化されたペアコンストラクターでは、引数が暗黙的にそれぞれの値型に変換可能である必要があります。 GCCはこれを許可しているようですが、これはバグです。 unique_ptrオブジェクトを手動で作成する必要があります。残念ながら、C++ 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;
}

さらに、insertの代わりに新しいemplace関数を使用しましょう:

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

emplaceは、両方の引数を対応するペアコンストラクターに転送します。

あなたが目撃した実際の問題は、そのようなオブジェクトが「移動可能」であるにもかかわらず、ペアコンストラクターがunique_ptrをコピーしようとしたことです。 GCCのC++ 0xサポートは、この状況を正しく処理するにはまだ十分ではないようです。私の知る限り、上からの私の線は、現在の標準ドラフト(N3126)に従って機能するはずです。

編集:実験的なC++ 0xモードでGCC4.5.1を使用する回避策として、次のことを試しました。

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

これも次の標準で機能するはずですが、GCCもそれを拒否します。 GCCがマップとマルチマップでunique_ptrをサポートするのを待つ必要があるようです。

28
sellibitze

Gcc4.6.0でunique_ptrをいじくり回しているところです。正しく機能する(非常に醜い)コードを次に示します。

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'はgcc4.6.0では実装されていません

1
karadoc

マップのemplaceメソッドを使用してみてください。

0
M.H.