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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

5269閲覧

C++ templateの使い方

opyon

総合スコア1009

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/10/26 02:35

編集2018/10/27 21:42

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

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

C++

1#include <cstdio> 2#include <iostream> 3#include <iterator> 4#include <string> 5#include <vector> 6#include <map> 7 8template <typename T1, typename T2> 9bool map_func_key(const std::map<T1, T2> &m, typename std::map<T1, T2>::key_type const &key) 10{ 11 return true; //処理省略 12} 13 14template <typename T1, typename T2> 15bool map_func_value(const std::map<T1, T2> &m, typename std::map<T1, T2>::mapped_type const &value) 16{ 17 return true; //処理省略 18} 19 20int main() 21{ 22 std::map<int, int> map1; 23 std::map<std::string, int> map2; 24 std::map<int, std::string> map3; 25 std::map<std::string, std::string> map4; 26 std::map<std::string, std::vector<std::string>> map5; 27 28 map1[1] = 111; 29 map2["b"] = 222; 30 map3[3] = "ccc"; 31 map4["4"] = "ddd"; 32 map5["5"] = {"555", "eee"}; 33 34 // map_func_key 35 std::cout << map_func_key(map1, 1) << std::endl; 36 std::cout << map_func_key(map2, "b") << std::endl; 37 std::cout << map_func_key(map3, 3) << std::endl; 38 std::cout << map_func_key(map4, "4") << std::endl; 39 std::cout << map_func_key(map5, "5") << std::endl; 40 41 // map_func_value 42 std::cout << map_func_value(map1, 111) << std::endl; 43 std::cout << map_func_value(map2, 222) << std::endl; 44 std::cout << map_func_value(map3, "ccc") << std::endl; 45 std::cout << map_func_value(map4, "ddd") << std::endl; 46 std::cout << map_func_value(map5, {"555", "eee"}) << std::endl; 47 48 getchar(); 49 return 0; 50}

###知りたいこと
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)

####サンプルコード

C++

1#include <cstdio> 2#include <iostream> 3#include <iterator> 4#include <string> 5#include <map> 6 7//template あり 8template <typename T1, typename T2> 9bool map_func1(const std::map<T1, T2> &m, T1 key) 10{ 11 return true;//処理 12} 13 14//template なし 15bool map_func2(const std::map<std::string,int> &m, std::string key) 16{ 17 return true;//処理 18} 19 20int main() 21{ 22 std::map<int,int> map1; 23 std::map<std::string,int> map2; 24 map1[1] = 111; 25 map2["a"] = 222; 26 27 // template あり ->OK 28 // <int,int> 29 std::cout << map_func1(map1,1) << std::endl; 30 31 // template あり ->ここだけNG 32 // <std::string,int> 33 std::cout << map_func1(map2,"a") << std::endl; 34 35 // template なし ->OK 36 // <std::string,int> 37 std::cout << map_func2(map2,"a") << std::endl; 38 39 getchar(); 40 return 0; 41}

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

これは

cpp

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

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

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

cpp

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

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

実行結果|Wandbox

追記

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

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

cpp

1typename std::map<int, std::string>::key_type // int 2typename 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>を推論し、第二引数の依存型名を決定するのです。

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

cpp

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

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

投稿2018/10/26 16:28

編集2018/10/27 21:17
mitama_rs

総合スコア165

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

opyon

2018/10/26 18:48 編集

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

2018/10/27 13:17 編集

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

2018/10/27 21:43 編集

追記での詳細な解説ありがとうございます。 >つまりkey_type, mapped_typeはstd::mapに依存した型名であり依存型名と呼ばれる 特にこの部分の説明がとても分かりやすく理解が深まりました。 mapped_typeの前にvalue_typeで実行するとエラーになっていたのも型が合わないからという認識で正しい気がします。 value_typeで何故エラーなのか疑問に感じていました。
opyon

2018/10/27 21:38

std::map https://cpprefjp.github.io/reference/map/map.html value_type 要素の型。std::pair<const Key, T> mapped_type 値の型。テンプレートパラメータ T これを見てなんとなくmapped_type 値の型 に変更すると動いたのも納得です。
guest

0

C++

1// 解決策その1 2std::cout << map_func1(map2,std::string("a")) << std::endl; 3 4// 解決策その2 5std::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 02:57

rtr1950x

総合スコア298

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

opyon

2018/10/26 03:04

回答ありがとうございます。 理解が深まりました。 型が違うエラーというのはなんとなくエラーから推察出来たのですが回避方法が分からず質問させていただきました。 >std::string("a") 惜しかったみたいです^^; 私はこうしてみてエラー回避できなかったです。 ↓ std::string "a"
opyon

2018/10/26 18:43

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

0

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

C++

1//template あり 2template <typename T1, typename T2, typename T3> 3bool map_func1(const std::map<T1, T2> &m, T3 key) 4{ 5 return true; 6} 7

投稿2018/10/26 02:57

opyon

総合スコア1009

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問