web-dev-qa-db-ja.com

マップC ++ STLのキーとしてのポインター

マップでキーとして使用する場合、カスタムオブジェクトへのポインターがどのように処理されるかについて質問があります。より具体的に定義する場合

std::map< CustomClass*, int > foo;

デフォルトのC++実装は、これらのポインターを処理するために機能しますか?それとも、それを処理するカスタムコンパレータ関数を定義する必要がありますか?一般に、オブジェクトへのポインターをキーとして使用するのは良い習慣ですか?

34
Ammar Husain

デフォルトの実装では、ポインタによって保存されたアドレスが比較されるため、異なるオブジェクトは異なるキーと見なされます。ただし、オブジェクトの論理状態は考慮されません。たとえば、std::string *をキーとして、2つの異なるstd::string同じテキストを持つオブジェクト"Hello"は別のキーと見なされます! (住所で地図に保存されている場合)

上記の重要な違いを理解している限り、ポインターをキーとして使用しても構いません。

50
Neil Kirk

ポインターは処理されますが、ポインター(メモリ順序)として比較されます。オブジェクトを比較する場合は、カスタムlessファンクターを渡す必要があります。

template<class T> struct ptr_less {
    bool operator()(T* lhs, T* rhs) {
        return *lhs < *rhs; }};
map<Object*,int,ptr_less<Object>> mymap;
22
firda

C++標準では、ポインタに対してstd::lessの特殊化が提供されているため、マップキーなどとして安全に使用できます。

7
Wojtek Surowka

これの合法性と可能なセマンティックの誤解は別として、すでに述べたように、ここでstd::mapではなくstd::unordered_mapを使用する理由は考えられません。 C++ 11より前のコンパイラを使用している場合、BoostおよびVisual C++でこれが早期にインターセプトされます。

ポインタを使用して一意のオブジェクトを表しているように見えるため、 boost :: flyweight のようなものを適用できます。

3
Steve Townsend

ポインターはキーとして使用できますが、特にstd :: map(またはstd :: set)を使用する場合はお勧めしません。 プログラムの動作は決定論的ではありません。つまり、マップ上で反復する場合、マップ内のアイテムが反復される順序は同じであるとは限りません。オブジェクト(キー)のメモリアドレスに本当に依存します。この例を見てください。マップへの挿入順序に関係なく、キーがポインターではなく文字列である場合、アイテムは決定論的に反復されます。

http://ideone.com/VKirct

#include <iostream>
#include <map>
using namespace std;

class SomeClass {
    public:
    SomeClass(const std::string& name): m_name(name) {}
    std::string GetName()const {return m_name; }
    bool operator <(const SomeClass& rhs) const { return m_name < rhs.m_name; }
    private:
    std::string m_name;
};

auto print_seq  = [](const auto& seq) { for (const auto& itr: seq) {std::cout << itr.second << " , ";} std::cout << std::endl;};

int main() {
    // your code goes here
    std::map<SomeClass*, std::string> pointer_keyed_map;
    SomeClass s3("object3");
    SomeClass s1("object1");
    SomeClass s2("object2");
    pointer_keyed_map.insert(std::make_pair(&s1, s1.GetName()));
    pointer_keyed_map.insert(std::make_pair(&s2, s2.GetName()));
    pointer_keyed_map.insert(std::make_pair(&s3, s3.GetName()));
    std::cout << "Pointer based keys: object order" << std::endl;
    print_seq(pointer_keyed_map);

    std::map<SomeClass, std::string> int_keyed_map;
    int_keyed_map.insert(std::make_pair(s3, s3.GetName()));
    int_keyed_map.insert(std::make_pair(s1, s1.GetName()));
    int_keyed_map.insert(std::make_pair(s2, s2.GetName()));
    std::cout << "String based keys: object order" << std::endl;
    print_seq(int_keyed_map);
    return 0;
}
3
blueskin