5

unordered_map を使用して、一定の時間 (平均的なケース) でメンバー変数によってオブジェクトにアクセスできるようにする良い方法はありますか? 次の例にはこの機能がありますが、それぞれの名前をPersonキーとして複製する必要があります。

#include <iostream>
#include <string>
#include <unordered_map>
#include <algorithm>

class Person {
 public:
  Person() : name_("") {}
  Person(const std::string& name) : name_(name) {}
  std::string getName() const { return name_; }
  void kill() const { std::cout << name_ << " is dead!" << std::endl; }
 private:
  std::string name_;
};

int main(int argc, const char* argv[]) {
  Person p1("dave");
  Person p2("bob");

  std::unordered_map<std::string, Person> map = {
    {p1.getName(), p1}, // Duplicating the
    {p2.getName(), p2}  // keys here
  };

  map["dave"].kill();
  return 0;
}

オブジェクトをハッシュしてアクセスするときに使用することを知る必要があるのではなく、どういうわけかそれ自体でvalue_typeある必要があると考えています。Personpair<string, Person>unordered_mapPerson::getName

理想的な解決策は、各オブジェクトのキーを取得するために使用することを知っているをセットアップできるようにすることですunordered_map(またはunordered_set、それが仕事により適している場合) 。Person::getName次に、オブジェクトを指定するだけでそれらを挿入し(キーの取得方法を知っているため、キーはありません)、の戻り値と等しいキーを指定してアクセスできますPerson::getName

次のようなもの:

// Pseudocode
std::unordered_map<Person, Person::getName> map = {p1, p2};
map["dave"].kill();

では、これをうまく実行できるテンプレート クラスをインスタンス化することは可能unordered_mapでしょうか?

4

3 に答える 3

3

Boostの使用に反対しない場合は、Boost.MultiIndexを使用すると、不必要な非効率性を追加することなく、これが非常に簡単になります。の値をキーとするunordered_setofオブジェクトを効果的に作成する例を次に示します。PersonPerson::getName()

#include <string>
#include <iostream>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/mem_fun.hpp>

namespace bmi = boost::multi_index;

struct Person
{
    Person() = default;
    Person(std::string const& name) : name_(name) { }
    std::string const& getName() const noexcept { return name_; }
    void kill() const { std::cout << name_ << " is dead!\n"; }

private:
    std::string name_;
};

typedef bmi::multi_index_container<
    Person,
    bmi::indexed_by<
        bmi::hashed_unique<
            bmi::const_mem_fun<Person, std::string const&, &Person::getName>
        >
    >
> PersonUnorderedSet;

int main()
{
    Person p1("dave");
    Person p2("bob");

    PersonUnorderedSet set;
    set.insert(p1);
    set.insert(p2);

    set.find("dave")->kill();

    // for exposition, find is actually returning an iterator
    PersonUnorderedSet::const_iterator iter = set.find("bob");
    if (iter != set.end())
        iter->kill();

    // set semantics -- newly_added is false here, because
    // the container already contains a Person named 'dave'
    bool const newly_added = set.insert(Person("dave")).second;
}

(効率のために、 の署名を値ではなく参照でPerson::getName()返すように変更したことに注意してください。ただし、変更は厳密には必要ありません。)const


トランスペアレント コンパレータに対する C++14 のサポートによりstd::unordered_set<Person>、Boost を必要とせずにここで使用できることに注意してください。

于 2011-08-16T23:43:42.580 に答える
2

明らかな解決策は、キー部分を複製して std::unordered_map;を使用することです。key == mapped.key_part小規模でローカライズされた使用の場合、これが最も単純で好ましい解決策ですが、コード内のいくつかの異なる場所で挿入が発生する可能性がある場合、コードを維持するのが難しくなるinvariant を強制するものは何もありません 。

値ではなくマップにポインターを保持している場合は、マップをラップして、キーが値の適切なフィールドへのポインターになるように調整できます。もちろん、これの問題は、値ではなくポインターを保持することを意味することです。

std::set値がマップに追加された後に値を変更しない場合は、 ではなくstd::mapを適切なハッシュ関数と等価関数と共に使用でき ます。これを行う場合、おそらくPersonデータにアクセスするためにダミー オブジェクトを作成する必要があります。

値を変更したい場合 (もちろんキーを除く)、そのメンバーへのアクセスstd::setのみを提供するという問題があります。constを使用してこれを回避できますがconst_cast、見苦しいです。(そしてエラーが発生しやすい。実際のキー部分が変更されていないことをどのように保証しますか。)

この解決策はどれも完全に満足できるものではありません。キー部分 (名前) への変更可能なアクセスがないあなたのようなケースでは、おそらく最初の 2 つのソリューションのいずれかをunordered_map使用し、独自のクラスでラップして、不変条件が維持されるようにします。

于 2011-08-16T09:39:32.977 に答える
1

あなたが探しているのはunordered_set、地図ではなく、です。セットは、値をキーと値の両方として使用します。

値の「キー」部分は変更しないでください。

于 2011-08-16T08:44:14.903 に答える