はじめに
Cに軽く触れた程度の知識でC++のライブラリの使い方を調べていたときに、テンプレートが良く出てきていました。その時は、テンプレートのことを良くわからず読み飛ばしていましたが改めて調べたところ、なかなか有用なものでしたので調べた内容を簡単なサンプルと使える例とともにまとめました。
環境
- ubuntu:18.04.2
- g++:7.4.0
テンプレート
テンプレートの説明
テンプレートについて、調べたときはテンプレートという名前に混乱させられました。いったんテンプレートという言葉の意味を忘れて読んでください。
テンプレートとは簡単に言うと型を意識しない関数(orクラス)になります。C++は変数や戻り値に型を指定するのが普通ですが、すべての型に適した関数(orクラス)を作成するのは無駄で無理なのでテンプレートという機能ですべての型を受け取る関数(orクラス)を作成できます。
テンプレートの形式
引数に適用するときの例
テンプレートを適用するのは簡単です。関数(orクラス)の上にtemplate <typename 変数名>
をつけるだけで、その関数(orクラス)の中では指定した変数名の型が呼び出し元に指定された型に変化します。
呼び出し方は普通の関数と同じようにするだけで呼び出せます。
※関数名<型名>(引数)でも呼び出せます。
template <typename 引数名>
void 関数名(引数名){
関数内の処理
}
// 呼び出し方
int main(){
int a = 1;
関数名(a);
関数名<int>(a);
}
戻り値に適用するときの例
戻り値の型を適用するときも同じく関数(orクラス)の上にtemplate <typename 変数名>
をつけるだけです。ここで少し注意しないといけないのがreturn
で返却する型になります。returnで返却する型は定義した戻り値の型と同じ出ないといけないため、下の例でいう戻り値名
を返却するようにしないといけないです。
template <typename 戻り値名>
戻り値名 関数名(){
戻り値名 a = 値;
関数内の処理
return a;
}
テンプレートのシンプルな例
この例ではどの型でも受け取れる引数Tを持つ関数を作成しています。関数の中身は変数Tの型を標準出力に表示しているだけです。
その関数にint型を与えたとき、std::string型を与えたとき、ポインタを与えたときの結果を表示しています。
#include <typeinfo>
#include <iostream>
#include <cxxabi.h>
template <typename T>
static void print_type(T){
int status;
std::cout << "type name:" << abi::__cxa_demangle(typeid(T).name(), 0, 0, &status) << std::endl;
}
int main(){
int a = 1;
print_type(a);
std::string b = "ab";
print_type(b);
char* e = "ab";
print_type(e);
}
結果
# ./cppMain
type name:int
type name:std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
type name:char*
結果を見ると、intなどの標準の型もstd::stringなどのクラスもポインタもすべて渡せていることがわかります。
今回は標準出力するだけの関数なのでイマイチ有用性がわかりませんが、次の例で実際に使える例を紹介します。
テンプレートの使える例
実際に使える例としては、以前まとめたboostpythonでC++とpythonの変換器を設定するの変換器を取り上げます。
以前の例ではint型限定の変換器でしたが、テンプレートを適用するとint型以外の変換もできるようになりました。また、リストを取得するget_list()
関数にもテンプレートを適用して同じ関数でありながら別の戻り値を返すようにしました。
これによって変換器をすべての型に作成する必要がなく、また関数も共通で使用できるものがあればpythonへの公開口を変えてあげるだけで対応できるようになりました。
適用後
#include <boost/python.hpp>
#include <iostream>
template <typename T>
struct vec_to_list_convert {
static PyObject* convert(std::vector<T> const& vec) {
boost::python::list pyList;
for (auto item: vec) {
pyList.append(item);
}
return boost::python::incref(pyList.ptr());
}
};
class Greeting {
public:
template <typename T>
std::vector<T> get_list() {
std::vector<T> cppVec;
cppVec.push_back(1);
cppVec.push_back(2);
return cppVec;
}
};
BOOST_PYTHON_MODULE(CppMod) {
boost::python::to_python_converter<const std::vector<int>, vec_to_list_convert<int>>();
boost::python::to_python_converter<const std::vector<double>, vec_to_list_convert<double>>();
boost::python::class_<Greeting>("greeting")
.def("get_int_list", &Greeting::get_list<int>)
.def("get_double_list", &Greeting::get_list<double>);
}
結果
# python3
>>> import CppMod
>>> a=CppMod.greeting()
>>> a.get_int_list()
[1, 2]
>>> a.get_double_list()
[1.0, 2.0]
おわりに
テンプレートをまとめました。知らなくても開発はできる程度の知識ですが、ライブラリの使い方の調査であったり効率的できれいなコードを意識した開発を実現するためにはとても有用なものでした。