質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.20%

C++ templateの使い方

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,995

opyon

score 987

解決!

回答頂いた@EnumHackさん、@rtr1950xさん、ありがとうございました。理解が深まりました。

リファレンスmap
イメージ説明

#include <cstdio>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <map>

template <typename T1, typename T2>
bool map_func_key(const std::map<T1, T2> &m, typename std::map<T1, T2>::key_type const &key)
{
    return true; //処理省略
}

template <typename T1, typename T2>
bool map_func_value(const std::map<T1, T2> &m, typename std::map<T1, T2>::mapped_type const &value)
{
    return true; //処理省略
}

int main()
{
    std::map<int, int> map1;
    std::map<std::string, int> map2;
    std::map<int, std::string> map3;
    std::map<std::string, std::string> map4;
    std::map<std::string, std::vector<std::string>> map5;

    map1[1] = 111;
    map2["b"] = 222;
    map3[3] = "ccc";
    map4["4"] = "ddd";
    map5["5"] = {"555", "eee"};

    // map_func_key
    std::cout << map_func_key(map1, 1) << std::endl;
    std::cout << map_func_key(map2, "b") << std::endl;
    std::cout << map_func_key(map3, 3) << std::endl;
    std::cout << map_func_key(map4, "4") << std::endl;
    std::cout << map_func_key(map5, "5") << std::endl;

    // map_func_value
    std::cout << map_func_value(map1, 111) << std::endl;
    std::cout << map_func_value(map2, 222) << std::endl;
    std::cout << map_func_value(map3, "ccc") << std::endl;
    std::cout << map_func_value(map4, "ddd") << std::endl;
    std::cout << map_func_value(map5, {"555", "eee"}) << std::endl;

    getchar();
    return 0;
}

知りたいこと

templateの使い方でどこが間違っているのか分かりません。
templateを使って型が<std::string,int>でも<int,int>でも動作するように汎用的にしたいです。
ご教示よろしくおねがいします。

現状

参考情報:適当なC++テンプレート入門

template<class T>と記述することでTを任意の型として関数内で使用できます。

リンク先の情報を参考にある関数を汎用的にしようとtemplateなるものを使ってみました。
主に使う型の組み合わせでテストしてみると<int,int>では上手くいったのですが、
<std::string,int>にすると型が違うとのエラーでコンパイルが通りません。

templateなしで<std::string,int>直接指定するとエラーは無く出来そうで出来ないので困っています。

エラー

no instance of function template "map_func1" matches the argument list -- argument types are: (std::map<std::__cxx11::string, int, std::less<std::__cxx11::string>, std::allocator<std::pair<const std::__cxx11::string, int>>>, const char [2])
template<class T1, class T2> bool map_func1(const std::map<T1, T2> &m, T1 key)

サンプルコード

#include <cstdio>
#include <iostream>
#include <iterator>
#include <string>
#include <map>

//template あり
template <typename T1, typename T2>
bool map_func1(const std::map<T1, T2> &m, T1 key)
{
    return true;//処理
}

//template なし
bool map_func2(const std::map<std::string,int> &m, std::string key)
{
    return true;//処理
}

int main()
{
    std::map<int,int> map1;
    std::map<std::string,int> map2;
    map1[1] = 111;
    map2["a"] = 222;

    // template あり ->OK
    // <int,int>
    std::cout << map_func1(map1,1) << std::endl;

    // template あり ->ここだけNG
    // <std::string,int>
    std::cout << map_func1(map2,"a") << std::endl;

    // template なし ->OK
    // <std::string,int>
    std::cout << map_func2(map2,"a") << std::endl;

    getchar();
    return 0;
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

これは

//template あり
template <typename T1, typename T2>
bool map_func1(const std::map<T1, T2> &m, T2 key)
{
    return true;
}


std::map<int, std::string> と const char*という引数にたいして

T2の推論がstd::stringconst char*という別々の型に推論されていることが原因ですが

//解決策その3(おそらくベストプラクティス)
template <typename T1, typename T2>
bool map_func1(const std::map<T1, T2> &m, typename std::map<T1, T2>::key_type const& key)
{
    return true;//処理
}

とすることにより、第一引数の推論から第二引数の型をkey_typeに固定することができます。

実行結果|Wandbox

追記

この原理は少々複雑です。
型推論について言及する必要があります。

まず以下のような型を依存型名といいます

typename std::map<int, std::string>::key_type // int
typename std::map<int, std::string>::mapped_type // std::string

これは型推論できないタイプの型です。

std::stringからstd::map<int, std::string>を導くのは不可能です。
std::map<int, std::string>という完全な型名がわかれば, key_type=int, mapped_type=std::stringが導けます。
つまりkey_type, mapped_typeはstd::mapに依存した型名であり依存型名と呼ばれるのはこのためです。

このテクニックのキモは依存型名が推論の対象から外れるというところです。

第一引数からstd::map<T1, T2>を推論し、第二引数の依存型名を決定するのです。

こうすることによって第二引数は依存型名に固定されるのです。

//template あり
template <typename T1, typename T2>
bool map_func1(const std::map<T1, T2> &m, T1 key)
{
    return true;
}

と書いたのではT1が別々に推論されるためkeyがT1に変換可能であっても型が合わなければコンパイルエラーとなってしまいます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/27 03:40 編集

    回答ありがとうございます。
    こちらの回答の方が汎用的且つkeyの型が保証される点で最適解だと思いました。

    関連した別件で質問しようか迷っていたのですがこちらの回答をヒントに解決出来た(?)ことがあるので是非確認だけしていただきたいのですが・・・
    keyをvalueに変えた場合に見よう見真似でstd::map<T1, T2>::value_typeだとエラーになりました。
    std::map<T1, T2>::mapped_type const& value
    とするとエラーなく動作しました。
    質問を編集して冒頭の「解決」欄にサンプルコードを追記しています。
    この使い方で合っているのでしょうか?
    偶然動いてるだけなのではないかと心配なのでもしよろしければアドバイス頂けると幸いです。

    キャンセル

  • 2018/10/27 20:45 編集

    詳しい原理について追記したため、ご査収いただきたく思います。

    キャンセル

  • 2018/10/28 06:33 編集

    追記での詳細な解説ありがとうございます。
    >つまりkey_type, mapped_typeはstd::mapに依存した型名であり依存型名と呼ばれる
    特にこの部分の説明がとても分かりやすく理解が深まりました。

    mapped_typeの前にvalue_typeで実行するとエラーになっていたのも型が合わないからという認識で正しい気がします。
    value_typeで何故エラーなのか疑問に感じていました。

    キャンセル

  • 2018/10/28 06:38

    std::map
    https://cpprefjp.github.io/reference/map/map.html
    value_type 要素の型。std::pair<const Key, T>
    mapped_type 値の型。テンプレートパラメータ T

    これを見てなんとなくmapped_type 値の型 に変更すると動いたのも納得です。

    キャンセル

+1

// 解決策その1
std::cout << map_func1(map2,std::string("a")) << std::endl;

// 解決策その2
std::cout << map_func1<std::string,int>(map2,"a") << std::endl;

「map_func1関数のテンプレート引数T1が、std::stringなのかconst char[2]なのかわからない」という趣旨のエラーですね。
原因は、第一引数map2のキーがstd::stringで、第二引数がconst char[2]である事です。

という訳で、第二引数をstd::stringにしてやるか(その1)、map_func1呼び出し時に明示的に型を指定してやるか(その2)になると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/26 12:04

    回答ありがとうございます。
    理解が深まりました。

    型が違うエラーというのはなんとなくエラーから推察出来たのですが回避方法が分からず質問させていただきました。
    >std::string("a")

    惜しかったみたいです^^;
    私はこうしてみてエラー回避できなかったです。

    std::string "a"

    キャンセル

  • 2018/10/27 03:43

    @rtr1950xさんせっかくベストアンサーにさせて頂いていたのにすみません。
    より汎用的で型が保証される回答がありましたのでBAを変更させていただきました。
    ご了承ください。

    キャンセル

0

これで良いかどうか分かりませんがエラーは無くなりました。

//template あり
template <typename T1, typename T2, typename T3>
bool map_func1(const std::map<T1, T2> &m, T3 key)
{
    return true;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.20%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる