24

私は次のようにC++0x unique_ptrクラス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 では次のエラーが表示されます (短縮されています。これは関連する部分だと思います。独自の 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
4

4 に答える 4

28

最初: あなたのクラス 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 モードで GCC 4.5.1 を使用して、回避策として次のことを試しました。

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

これは、今後の標準でも動作するはずですが、GCC も拒否しています。GCC がマップとマルチマップで unique_ptr をサポートするまで待つ必要があるようです。

于 2010-10-11T13:31:57.603 に答える
6

unique_ptrまだaを正しく使用できるとは思えmap::insertません。GCC のバグは次のとおりです。

バグ 44436 - [C++0x]連想コンテナーおよび順序付けされていないコンテナーでinsert(&&)andを実装するemplace*

GCC 4.6 で修正される可能性があるようですが、確認のためにバニラ Ubuntu 10.04.1 で SVN からビルドすることはできません。

Update0

これは、GCC svn r165422 (4.6.0) では機能しません。

于 2010-10-12T15:19:40.823 に答える
1

gcc 4.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」は gcc 4.6.0 では実装されていません

于 2011-06-01T03:12:07.030 に答える