2

std::map コンテナがどのように実装されているかを理解するのを手伝ってくれる人はいますか? アトミック メンバーを含むクラスがあり、コピー コンストラクターを呼び出す必要がないため、c++11 の削除演算子を使用して、コピー コンストラクターの暗黙的な生成を抑制します。

MyCalss(const MyClass& a) = delete;

これは私の Windows ビルドでは問題なく動作しましたが、Linux では std::map クラスの [] 演算子が削除された関数を呼び出そうとしていることを知らせるエラーが表示されます。

Windows VS2013 と Linux GCC 4.7.x の map の実装には大きな違いがあるようです。これにより、オブジェクトをマップに挿入する方法に関する実験を行うことができました。

私はこの小さなサンプルプログラムを書きました:

#include <stdlib.h>
#include <stdio.h>
#include <map>
#include <iostream>
#include <string>

using namespace std;
class TestItem {
public:
TestItem () { 
    _name = "TestItem" + id();
    cout << "Constructing " << _name << endl;
}
TestItem (const TestItem & other) {
   _name = "TestItem " + id();
   cout << "Copying " << other._name << " to new  " << _name <<endl;
}

string id() 
{
   static int id = 0;
   char buf[2];
   sprintf_s(buf, "%d", id++);
   return string(buf);
}
~TestItem(){
   cout << "Destroying " << _name << endl;
}
void doStuff()
{
   // stub
}

string _name;
};

void run()
{
   cout << "making new obj" << endl;
   TestItem a;
   cout << endl << endl;

   map<string, TestItem> TestItemMap;
   cout << "Makeing new obj as part of a map insert" << endl;
   TestItemMap["foo"].doStuff();
   cout << endl << endl;

   cout << "adding a value to the map" << endl;
   TestItemMap["new foo key"] = a;
   cout << endl << endl;

   cout << "looking up a value that has already been inserted" << endl;
   TestItem& b = TestItemMap["foo"];
   cout << endl << endl;
}
int main(int argc, char** argv)
{
   run();
}

Windows でこのプログラムを実行すると、次の出力が得られます。

making new obj
Constructing TestItem0

Making new obj as part of a map insert
Constructing TestItem1

adding a value to the map
Constructing TestItem2

looking up a value that has already been inserted

Destroying TestItem1
Destroying TestItem0
Destroying TestItem0

これは、私が書くときに内部的に見られると期待するものです

 TestItemMap["foo"].doStuff();

マップが TestItem の新しいインスタンスを作成し、ツリー ノードを新しい TestItem に内部的にリンクすることによって、それを RedBlack Tree に挿入すると予想します。

ただし、この同じコードを Linux で実行すると、結果は大きく異なります。

making new obj
Constructing TestItem0

Making new obj as part of a map insert
Constructing TestItem1
Copying TestItem1 to new TestItem2
Copying TestItem2 to new TestItem3
Destroying TestItem2
Destroying TestItem1

adding a value to the map
Constructing TestItem4
Copying TestItem4 to new TestItem5
Copying TestItem5 to new TestItem6
Destroying TestItem5
Destroying TestItem4

looking up a value that has already been inserted

Destroying TestItem0
Destroying TestItem3
Destroying TestItem0

これは、[] 演算子が TestItem の新しいインスタンスを作成し、次に外部 map.insert() 関数を呼び出してから、新しく作成された TestItem を破棄していることを示しており、コピー コンストラクターへの呼び出しの 1 つだけを説明しています。gcc の c++ stdlib は本当に非効率的ですか?

この問題を克服するために人々が使用する標準的なトリックはありますか?

4

2 に答える 2

3

sprintf_sまず、その恐ろしいことを修正しました。

string id() 
{
   static int id = 0;
   std::stringstream s;
   s << id++;
   return s.str();
}

また、「既に挿入されている値を検索する」を実際に実行するように変更しました[編集:そしてあなたもそうしました:-)]

ここで、C++03 モードで g++ 4.8.1 を使用してコンパイルすると、あなたと同様の結果が得られます。しかし、でコンパイルすると-std=c++11、私は得る

making new obj
Constructing TestItem0

Making new obj as part of a map insert
Constructing TestItem1

adding a value to the map
Constructing TestItem2

looking up a value that has already been inserted

Destroying TestItem0
Destroying TestItem1
Destroying TestItem0

MSVC は自動的に C++11 機能 (セマンティクスを移動する可能性が最も高い) を使用してパフォーマンスを向上させているようですが、g++ に同じことを明示的に指示する必要があります。

于 2013-11-04T10:48:55.883 に答える
2

GCC 4.8で修正されたバグのようです。

  • ここではGCC 4.8で動作します
  • ここでは、GCC 4.7 でのコンパイルに失敗します
于 2013-11-04T10:43:30.937 に答える