テンプレートクラス内のusingによるエイリアス宣言について

背景

以下のようなテンプレートクラスにおいて、
使用する型を短縮したい場合がある。
例えばコンテナ型を用いる場合、テンプレートが入れ子になって
ごちゃごちゃして見づらい。

template <class T, class K>
class ScoreTable
{
public:
  // 型名が長すぎる
  using Dmap = std::unordered_map<T, std::unordered_map<K, int>>;
  int GetScore(T row, K col);
  void SetScore(T row, K col, int val);
  void SetDmap(Dmap &dmap);

private:
  std::unordered_map<T, std::unordered_map<K, int>>dmap_;
};

これをこうしたい。

// どこかで以下を宣言
using Dmap = std::unordered_map<T, std::unordered_map<K, int>>;
// 型がすっきりに
template <class T, class K>
class ScoreTable
{
public:
  int GetScore(T row, K col);
  void SetScore(T row, K col, int val);
  void SetDmap(Dmap &dmap);

private:
  Dmap dmap_;
};

問題点

テンプレートクラスで使うためにはグローバル空間で
using宣言はできない?と考えclass内でusing宣言にする作戦。
ただ、void SedDmap()関数は引数に Dmap型を使うため、
外部にDmap型を知らせるのかわからなかった。

結論

classのpublicに型エイリアスを宣言する。
エイリアスをクラスから参照できることを知らなかった。

適当にクラス内で宣言して使っていたが、
privateになっていることが頭から抜けていた。

ソースコード

double_map.h

#ifndef DOUBLE_MAP_H_
#define DOUBLE_MAP_H_

#include <unordered_map>

template <class T, class K>
class ScoreTable
{
public:
  using Dmap = std::unordered_map<T, std::unordered_map<K, int>>;
  int GetScore(T row, K col);
  void SetScore(T row, K col, int val);
  void SetDmap(Dmap &dmap);

private:
  Dmap dmap_;
};

template <class T, class K>
int ScoreTable<T, K>::GetScore(T row, K col)
{
  return dmap_.at(row).at(col);
}

template <class T, class K>
void ScoreTable<T, K>::SetScore(T row, K col, int val)
{
  dmap_.insert({row, {}});
  dmap_[row].insert({col, val});
}

template <class T, class K>
void ScoreTable<T, K>::SetDmap(Dmap &dmap)
{
  dmap_ = std::move(dmap);
}
#endif

上記クラスを使う側

double_map.cc

#include <iostream>
#include <string>
#include "double_map.h"

int main()
{
  using Stbl = ScoreTable<std::string, std::string>;
  Stbl dmap;
  Stbl::Dmap set_map = {{"Tom", {{"english", 95}, {"math", 75}}},
                        {"Jaws", {{"english", 63}, {"math", 52}}}};
  dmap.SetDmap(set_map);
  std::string student = "Tom";
  std::string subject = "math";
  std::cout << student << " : " << subject << " : score : ";
  std::cout << dmap.GetScore(student, subject) << std::endl;

  return 0;
}

出力

Tom : math : score :75

using template type parameter ‘T’ after ‘class’ エラーについて

発端

C++テンプレートの勉強をしていた時の備忘録
試しに二次元連想配列(キーを二つ持つ配列)をテンプレートクラスを
使ったところ、コンパイルエラーが発生した
以下コンパイルエラーの一部

double_map.h:18:21: error: using template type parameter ‘T’ after ‘class’
   18 | int DoubleMap<class T, class K>::GetValue(T row, K col)

原因

メンバ関数の定義の際に、'class' をつけてしまっていた。
こんなミスだったなんて・・・

template <class T, class K>
int DoubleMap<class T, class K>::GetValue(T row, K col)
{
  return map_.at(row).at(col);
}

上記のint DoubleMap< class T, class K>部分
定義の際にはint DoubleMap< T, k>で良い。

template <class T, class K>
int DoubleMap<T, K>::GetValue(T row, K col)
{
  return map_.at(row).at(col);
}

結論

エラーはよく読もう

ソースコード

#ifndef DOUBLE_MAP_H_
#define DOUBLE_MAP_H_

#include <unordered_map>

template <class T, class K>
class DoubleMap
{
public:
  int GetValue(T row, K col);
  void SetValue(T row, K col, int val);

private:
  std::unordered_map<T, std::unordered_map<K, int>> map_;
};

template <class T, class K>
int DoubleMap<class T, class  K>::GetValue(T row, K col)
{
  return map_.at(row).at(col);
}

template <class T, class K>
void DoubleMap<class T, class K>::SetValue(T row, K col, int val)
{
  map_.insert({row, {}});
  map_[row].insert({col, val});
}

#endif

正しくはこちら

#ifndef DOUBLE_MAP_H_
#define DOUBLE_MAP_H_

#include <unordered_map>

template <class T, class K>
class DoubleMap
{
public:
  int GetValue(T row, K col);
  void SetValue(T row, K col, int val);

private:
  std::unordered_map<T, std::unordered_map<K, int>> map_;
};

template <class T, class K>
int DoubleMap<T, K>::GetValue(T row, K col)
{
  return map_.at(row).at(col);
}

template <class T, class K>
void DoubleMap<T, K>::SetValue(T row, K col, int val)
{
  map_.insert({row, {}});
  map_[row].insert({col, val});
}

#endif