hoge diary - December 17, 2006

[C++] std::map::at() の代替

g++-4.1.1 付属の libstdc++ には,std::map::at() というメンバ関数がついています.これは,引数として与えたキーに対応する要素を参照するものですが,operator[] と違うのは,未登録のキーを参照しようとすると,std::out_of_range 例外が投げられるという点です.

g++-4.1.1 で開発しているときにこの関数を使用して何事もなくコンパイルできていましたが,g++-3.4.4 な環境でコンパイルしてエラーが出るので調べてみました.

その結果,g++-4.1.1 の stl_map.h:353 には次のように書いてあり,at() メンバ関数は非標準であることが判明しました.

// DR 464. Suggestion for new member functions in standard containers.

非標準のメンバ関数を使うのもいささか気がひけるので(と言いつつ,Boost は使いまくっていますが),代替の関数 Map_at() を書くことになりました.様々な型のペアに対応できるように,テンプレート関数で書くことにします.そして,最初に書いた定義は以下の通り.左端の数字は行番号です.

 5 template <typename map_type>
 6 map_type::mapped_type& Map_at(map_type& m, const map_type::key_type& key)
 7 {
 8   map_type::iterator it = m.lower_bound(key);
 9   if (it == m.end() || m.key_comp()(key, (*it).first))
10     throw std::out_of_range("Map_at");
11   return (*it).second;
12 }

g++-4.1.1 でコンパイルしてみると... 以下の通りのエラーメッセージが登場.

test.cc:6: error: expected constructor, destructor, or type conversion before '&' token

テンプレート内部において,型の周りで expected 〜 なコンパイルエラーが発生する場合,経験上 typename が抜けているので,勘と試行錯誤を組み合わせて typename を補います.そしてできたのが次のコード.

 5 template <typename map_type>
 6 typename map_type::mapped_type& Map_at(map_type& m, const typename map_type::key_type& key)
 7 {
 8   typename map_type::iterator it = m.lower_bound(key);
 9   if (it == m.end() || m.key_comp()(key, (*it).first))
10     throw std::out_of_range("Map_at");
11   return (*it).second;
12 }

これでめでたくコンパイルが通りました.しかし,まさか 3 箇所も typename を書き加えることになるとは.

今回の例では,C++ Labyrinth にあるような,ポインタ型のつもりで * 記号を書いているわけでもなければ,typedef しているわけでもなかったので,2 個目の typename や 8 行目の typename がなくとも,構文から map_type::key_type や map_type::iterator がそれぞれ引数もしくは変数の型だと判断してくれると期待していたのですが,どうやらこの場合も明示しないとダメみたいです.私にはまだまだ C++ の修行が足りないようです.

それはさておき,コンパイルが通ってしまえば,それを使うのは非常に楽です.以下のような感じで使用します.

std::map<int, int> map_a;
std::map<const char*, int> map_b;

map_a.insert(std::make_pair(10, 20));
map_b.insert(std::make_pair("hoge1", 10));

try
{
  std::cout << Map_at(map_a, 10) << std::endl;
  std::cout << Map_at(map_b, "hoge1") << std::endl;
  std::cout << Map_at(map_b, "hoge2") << std::endl;
}
catch (std::out_of_range& e)
{
  std::cerr << e.what() << std::endl;
}

上記コードの実行結果です.

20
10
Map_at

std::map::at() が標準になるまではこの関数を使うことにします.

コメント

名前(何でも可):

テキスト(http:// を含む内容は投稿できません):

トラックバック

トラックバック URI: https://www.pakunet.jp/hoge/trackback/2006121701

トラックバックはありません.


Valid XHTML 1.1! Valid CSS!
© 2004-2009 ぱくちゃん.
Last modified: Fri Nov 02 08:58:03 JST 2007