あめだまふぁくとりー

Boost.Graphとかできますん

コンテナとは違うのだよコンテナとは

はにゃー.修論提出・発表と論文提出が被って停滞してしまっていました.修論まだ提出できる段階じゃないけど徐々に復活でふ!

What is the difference between a std::map and a boost::associative_property_map ?
When a nonexisting key value is given (e.g. "khwgfruiwgp" in the example above), the boost::associative_property_map's internal asserts will find it. For the std::map you have to write these asserts yourself every time.

You cannot iterate through a boost::associative_property_map, where with a std::map you can.

boost::associative_property_mapはstd::mapをLvaluePropertyMapに変換するアダプタクラス.公式ドキュメントにもサンプルコードがあるのだけれども,これだけ見ちゃうとstd::mapにgetとputを適用可能にしただけに見えて有り難味を感じないのよね.
http://www.boost.org/doc/libs/1_48_0/libs/property_map/doc/associative_property_map.html
例えば,

template <class AddressMap, class Key, class Value>
void write(AddressMap& address_map, const Key& key, const Value& value)
{
    address_map[key] = value;
}

のようなoperator経由で値を書き込む関数を定義すると,std::mapにもassociative_property_mapにもoperatorが定義されているので,

int main()
{
    typedef std::map<std::string, std::string> Name2Address;
    Name2Address name2address = {
        {"Fred", "710 West 13th Street"},
        {"Joe",  "710 West 13th Street"}
    };

    typedef boost::associative_property_map<Name2Address> AddressMap;
    AddressMap address_map(name2address);    // PropertyMap

    write(name2address, "Fred", "325 Cushing Avenue");
    write(address_map,  "Joe",  "325 Cushing Avenue");

のように呼び出すことができます.
これだと上のように何が違うのっていう疑問がでても仕方がないかなと思います.

しかしstd::mapを直接書き換えるか,associative_property_mapを通して書き換えるかはカプセル化の話を抜きにしても実際は全く違います.

const性の違い

例えば例えば,std::mapのインスタンスのconst参照を先ほどの書き込み関数に渡した場合,

    const Name2Address& const_name2address = name2address;
    write(const_name2address, "Fred", "Dokoka To-ku");    // エラー!

になるのは当然ですね?(書き込み以前にoperator[]を呼び出せませんが)
一方,PropertyMapインスタンスのconst参照を渡すと,

    const AddressMap& const_address_map = address_map;
    write(const_address_map, "Joe", "Dokoka To-ku");     // エラー...じゃない!

ちゃんと書き換えれます!不思議!

値渡しの違い

今度は先ほどのwriteの値渡しバージョンを使います.

template <class AddressMap, class Key, class Value>
void write_by_value(AddressMap address_map, const Key& key, const Value& value)
{
    address_map[key] = value;
}

std::mapに対して呼び出しても,

    write_by_value(name2address, "Fred", "Arumikan No Ue");
    std::cout << name2address["Fred"] << std::endl;    // "710 West 13th Street"を出力

もちろん値は書き換わりません.
しかし,PropertyMapに対しては,

    write_by_value(address_map, "Joe", "Arumikan No Ue");
    std::cout << address_map["Joe"] << std::endl;      // "Arumikan No Ue"を出力!
    assert(address_map["Joe"] == name2address["Joe"]);

書き換わってしまいます!

まとめ

このようなconst渡し,値渡しによる値の変化はassociative_property_mapだけでなく,他のプロパティマップの場合でも同様に起こります.これはPropetyMapがstd::mapのような値の集合を持つコンテナではないことを意味しています.
つまり,↓このようにPropetyMapから直接値を取り出すような関係でなく,

↓このようにPropertyMapはkeyとContainer(もちろんContainerの部分は関数でもなんでもかまいません)とを仲介するような関係になっています.

PropetyMapはkeyと値をどのように対応づけるか,どのように取り出すかしか関係しません.そのためPropetyMap自身のconst性はContainer自身のconst性には関係せず,PropetyMapのコピーでも同じContainerを参照できます.

PropertyMapのこのconst性のため,const関数でPropertyMapを用いると意図しない値の書き換えが起きてしまいそうですが,この場合,ReadOnlyのPropertyMapの使用を強制することで問題を回避できます.PropertyMapは一つのContainerに対して複数定義することができるのです.これが上の図にPropertyMapを二つ書いた理由にもなるのですが,一方はReadWrite可能にし,もう一方はReadOnlyに限定するということができるのです.

PropertyMapについての利点,概念等はCryolite先生がわかりやすく纏めてくれているので,そちらは見ましょう!
http://www.slideshare.net/Cryolite/boostpropertymap-pptx