渡米生活。(日記)

渡米生活。本家から切り離しました。あまり渡米生活に関係のないプログラムネタや音楽ネタなど。

C++ std::mapでconstがどうの、と怒られる件

ものすごく初歩的な話なのだけど、久々にハマった(しかも2度目)ので忘れないよう記録。

std::mapで、コンパイル時にこういう怒られ方したことないですか?

error: passing 'const std::map' as 'this' argument of .... discards qualifires

....の部分は面倒なのではしょってしまいましたが。
何をやって怒られたかというと、こんな感じのコードです。

class MyClass {

  double weightA_;
  double weightB_;

  void RestoreWeight(const std::map<std::string, double> &wmap)
  {
      weightA_ = wmap["A"];
      weightB_ = wmap["B"];
  }
};

何がイカンかというと、mapのオペレータはconstがついてるmapには使えない、ということです。
ちょっと考えれば当たり前で、mapの
オペレータというのは、引数で与えるキーに対応するエントリがない場合、そのキーのためのエントリを作るんだから、const functionになりようがないんですよね。

でも、個人的には、const 戻り値 operator[](キー)const みたいなのも存在してそうな気がしてしまうので、こういう失敗をする、と……。

というわけで、上の例は、このように書くしかなさそう。

class MyClass {

  double weightA_;
  double weightB_;
 
  double GetValue(const std::map<std::string, double> &wmap,
                 const std::string &key)
  {
     std::map<std::string, double>::const_iterator i = wmap.find(key);
     if (i == map.end()) return 1.0; // default weight
     return i->second;
  }

  void RestoreWeight(const std::map<std::string, double> &wmap)
  {
      weightA_ = GetValue(wmap, "A");
      weightB_ = GetValue(wmap, "B");
  }
};

constは正直うざいけど、上の怒られるコードが許されたら多分計算結果は怪しいことになるので、こういうときC++比較的安全だな、と思う。。

追記。

上記GetValue関数をテンプレートにして、どこでも使えるようにしようとしてハマった。

#include <map>

namespace Utils {

  template <class X, class Y>
  const Y &GetMapValue(const std::map<X,Y> &m, X x)  const
  {
      std::map<X,Y>::const_iterator i = m.find(x);
      if (i == m.end()) {
         log_error("Failed to extract map value.");
      }
      return i->second;
  }

}

これをコンパイルすると、こんな感じで怒られます。

    error: expected ‘;’ before ‘i’

なんで';'が必要やねん、とえらくハマったら、要するに、std::mapが入ってるから型名と認識できなかった、ということみたい。

というわけで、その行を

    typename std::map<X,Y>::const_iterator i = m.find(x);

としたら、とりあえずコンパイルは通った。これでまともに動くかは現在テスト中。