15

私は次のコードを持っています:

#include <functional>   // std::less
#include <map>
#include <iostream>
using namespace std;

class Key
{
public:
        Key() {cout << "Key Constructor" << endl;}
        ~Key() {cout << "Key Destructor" << endl;}
        Key(const Key& key) {cout << "Key Copy Constructor" << endl;}

        bool operator < (const Key& k1) {return true;}
};
int main()
{
        map<Key, int> mymap;
        Key k;

        cout << "operator[]"<<endl;
        mymap[k] = 1;

        map<Key, int> mymap2;
        cout << "insert"<<endl;
        mymap2.insert(std::make_pair(k, 1));
        cout << "=========" << endl;

}

出力は次のとおりです。

$ g++ test.cpp -fpermissive
$ ./a.out
Key Constructor
operator[]
Key Copy Constructor
Key Copy Constructor
Key Destructor
insert
Key Copy Constructor
Key Copy Constructor
Key Copy Constructor
Key Copy Constructor
Key Destructor
Key Destructor
Key Destructor
=========
Key Destructor
Key Destructor
Key Destructor

mymap[k] = 1; の理由を誰か説明してください。2 つのコピー コンストラクターと mymap2.insert(std::make_pair(k, 1)); を呼び出します。4 つのコピー コンストラクターを呼び出しますか? それは operator[] が挿入よりもはるかに効率的であることを意味しますか?

ありがとう。

概要:

ユーザー 6502 と petersohn の洞察に感謝します。挿入用の 2 つの余分なコピー コンストラクターの理由は次のとおりだと思います。

  • make_pairは関数です。最初に関数内でコピーを作成し、次にそのコピーを返します。これは 1 つの余分なコピーです。
  • make_pair(k, 1) は を作成しますpair<Key, int>が、必要なvalue_typeは ですpair<const& Key, int>。型変換により別の余分なコピーが発生します

したがって、ケース 2 で使用する場合:

mymap2.insert(std::pair<const Key, int>(k, 1));

呼び出されるコピー コンストラクターの数は、operator[] と同じになります。

6502 が指摘したように、次の主張は変更されたため、もはや真実ではありません。

この関数 (operator[]) の呼び出しは、(*((this->insert(make_pair(x,mapped_type()))).first)).second と同等です。

operator[] は、make_pair() によって導入される余分なコピーを避けるために、別の方法で実装されています。

4

2 に答える 2

12

挿入の問題make_pairは、間違った型のペアが作成されるため、渡されたオブジェクトを適切なペア型に変換して に渡す必要があることinsertです。

実際、以前のバージョンでは、C++ 標準は次のようmap::operator[]に動作することが義務付けられていましたinsert(したがって、非効率的な実装が強制されます)。その後、テキストが緩和され、より良い実装が可能になりました。

とにかく、標準コンテナの場合、要素は「値」と見なされることに注意してください。つまり、実装は自由にコピーでき、コピーがいくつ作成されるかは保証されません。

make_pairコードを次のように変更すると、問題を確認できます。

    mymap2.insert(std::pair<const Key, int>(k, 1));

長い説明

問題は、値make_pairを作成しますが、 署名は代わりに(最初の要素とのペアであることに注意してください) を必要とすることです。std::pair<Key, int>insertconst std::pair<const Key, int>&std::map::value_typeconst

これら 2 つのタイプは互換性がなく、関連性がないため、呼び出しを行うには、キーと値の両方をコピーして別のペアを作成する必要があり、ここで余分なキーの重複が発生します。

pair<X, Y>が期待される場所で a を直接使用できることが明らかに「論理的」である可能性があるとしても、pair<const X, Y>これは C++ では当てはまりません。

于 2013-06-22T06:38:34.600 に答える
2

のせいですmake_pair。ペアにコピーkする必要があり、さらに追加の関数呼び出しがあります。おそらく、最適化を有効にすることでコピーの数を減らすことができます(私は試していません)。ただし、これを行うと、operator[] を使用した場合とまったく同じ数のコピーが得られます。

mymap2.insert(std::pair<const Key&, int>(k, 1));

しかし、C++11 では状況が改善され始めています。コードを C++11 でコンパイルすると、挿入された 2 つのコピーが得られます。さらに良いことにKey、move コンストラクターがある場合は、挿入用に 1 つのコピーと 1 つの move を取得します (一方、operator[] では 2 つのコピー)。上記の行を C++11 で使用すると、移動を惜しまず、コピーを 1 つだけ取得できます。

興味深いことに、operator[] を使用すると、常に 2 つのコピーが作成されますが、正確な理由はわかりません。

于 2013-06-22T06:45:40.580 に答える