Templateの基礎です。
なんだかんだで、勉強不足で知らないことが多かったのでまとめたいと思います。
なんだかんだで、勉強不足で知らないことが多かったのでまとめたいと思います。
まず、引数の型からテンプレートパラメータが自動決定される、よくあるテンプレート関数です。
上記の場合、func1(100)などとすれば型が自動確定しますが、func1<char>(100)などとして、明示的にテンプレートパラメータで指定することもできます。
次は明示的なテンプレートパラメータ指定が必須な場合です。
戻り値にテンプレート型が使用されており、引数による型の自動特定はできません。
上記を使用する場合、func2<char>(100)などのようにして明示的にテンプレートパラメータを示さないとエラーになります。
また、下記のようにテンプレートパラメータが2つ以上ある場合においては、明示と省略表現を混在できます。
また、下記のようにテンプレートパラメータが2つ以上ある場合においては、明示と省略表現を混在できます。
例えば、func3<char>(100)とも、func3<char,int>(100)とも書けます。
さらに、次のような型を指定したテンプレート関数を作ることもできます。
さらに、次のような型を指定したテンプレート関数を作ることもできます。
使うときにはfunc4<2>(10)などとします。
当然ですが、テンプレート関数はコンパイル時に静的に生成されるため、func4<a>(10)のように、テンプレートパラメータに変数を指定することはできません。
動的なパラメータは大人しく引数で渡して下さい。
しかし、ここで挙げたサンプルだけ見ると、テンプレートの利点がよく飲み込めないですね。
#defineなどのプリプロセッサと本質何が違うのでしょうか?なんとなくかっこ良く見えるとか?
実際書いててよくわからなくなってきました。
例えば、テンプレートの場合は、型がより安全に扱えるという利点があると思います (プリプロセッサは単なる文字列の置換にすぎないので)。 しかし、Policy-based Designのような技巧的な手法を使わないとtemplateの本領は見えてこないのかもしれないです。
プログラム中にはなるべく即値(イミディエイト)を書くべからずとはよく言われますが、そういう意味では#defineもtemplateも発想の着眼点は同じなのかもしれません。
例えば、テンプレートの場合は、型がより安全に扱えるという利点があると思います (プリプロセッサは単なる文字列の置換にすぎないので)。 しかし、Policy-based Designのような技巧的な手法を使わないとtemplateの本領は見えてこないのかもしれないです。
プログラム中にはなるべく即値(イミディエイト)を書くべからずとはよく言われますが、そういう意味では#defineもtemplateも発想の着眼点は同じなのかもしれません。
ヤンバル戦線の更新楽しみにしています!
返信削除今回のテンプレートの基礎のお話、参考になりました。
話の中で言及されているテンプレートとdefineの類似点という観点、なるほどなと思いました。
考えてみれば、defineなどのプリプロセッサを使えばC言語でもC++のテンプレートライクな記述はできそうだなと。
試しに以下のテストプログラムを作ってみました。
■ a.h
#include
HOGE * new_mem(void)
{
HOGE * h;
h = (HOGE *)malloc(sizeof(HOGE));
return h;
}
void rel_mem(HOGE ** h)
{
free(*h);
*h = NULL;
}
■ main.c
#include
typedef struct
{
int a;
}s_a;
#define HOGE s_a
#include "a.h"
void main(void)
{
s_a * v1;
v1 = new_mem();
v1->a = 100;
printf("%d\n", v1->a);
rel_mem(&v1);
}
ほうほう。
確かにこれなら型を目的に応じて変更したい汎用関数に対して、void * ではなく、コンパイル時に静的に型を決定することができますね。
(まぁ、C言語の型安全は弱いので、ここまでする意味はあるのかという疑問はありますが。)
また関数にstaticを指定してファイル内でのみ有効の関数にすれば、プロジェクト内に型別に応じた複数の関数を定義することもできますね。
今回の投稿はC言語の奥深さを再認識させてもらえた大変有意義なものでした。
しかしながら、同じファイル内に2つ以上の型が異なる関数をC言語のdefineで作る場合にはどうすればいいんでしょうね。
返信削除C++のように関数のオーバーロードや名前空間などがあればできるんですけどね。
残念ながらこれがC言語の限界なんだろうか。
同じファイル内に2つ以上の型が異なる関数をC言語のdefineで作る1つの方法として、関数名を装飾するようなマクロを作ればいいのではないかという結論に達しました。
返信削除サンプルプログラム:
■ a.h
#include
#define newmem(x) new_mem##x
#define relmem(x) rel_mem##x
static HOGE * NEWMEM(void)
{
HOGE * h;
h = (HOGE *)malloc(sizeof(HOGE));
return h;
}
static void RELMEM(HOGE ** h)
{
free(*h);
*h = NULL;
}
■ main.c
#include
typedef struct
{
int a;
}s_a;
typedef struct
{
double a;
}s_b;
#define newmem_a newmem(a)
#define relmem_a relmem(a)
#define NEWMEM newmem_a
#define RELMEM relmem_a
#define HOGE s_a
#include "a.h"
#undef NEWMEM
#undef RELMEM
#undef HOGE
#define newmem_b newmem(b)
#define relmem_b relmem(b)
#define NEWMEM newmem_b
#define RELMEM relmem_b
#define HOGE s_b
#include "a.h"
#undef NEWMEM
#undef RELMEM
#undef HOGE
void main(void)
{
s_a * v1;
s_b * v2;
v1 = newmem_a();
v2 = newmem_b();
v1->a = 100;
printf("%d\n", v1->a);
v2->a = 1.5;
printf("%lf\n", v2->a);
relmem_a(&v1);
relmem_b(&v2);
}
うむうむ。
多少まどろっこしいところがありますが、一応目的は達成できましたね。
まあでも、やはり言語レベルでテンプレートといった仕組みを備えているものには、記述の簡潔さという点では勝てませんね。
あれ、#include の一部が消えている。。。
返信削除a.h の#include は malloc.hで、
main.c の#include は stdio.h です。
HTMLのタグとして解釈された・・・?
このコメントは投稿者によって削除されました。
返信削除アイディアをコメントいただきありがとうございます。
返信削除確かにこれなら、Cでも似たようなことができますね。
以下のようにするとさらに使いやすくなると思いました。
■a.h -----------------------------------
#if defined(HOGE) && defined(NEWMEM) && defined(RELMEM)
#include <stdio.h>
static HOGE * NEWMEM(void)
{
HOGE * h;
h = (HOGE *)malloc(sizeof(HOGE));
return h;
}
static void RELMEM(HOGE ** h)
{
free(*h);
*h = NULL;
}
#undef NEWMEM
#undef RELMEM
#undef HOGE
#endif
■main.c --------------------------------
#include <stdio.h>
typedef struct
{
int a;
}s_a;
typedef struct
{
double a;
}s_b;
#define HOGE s_a
#define NEWMEM newmem_a
#define RELMEM relmem_a
#include "a.h"
#define HOGE s_b
#define NEWMEM newmem_b
#define RELMEM relmem_b
#include "a.h"
int main(void)
{
s_a * v1;
s_b * v2;
v1 = newmem_a();
v2 = newmem_b();
v1->a = 100;
printf("%d\n", v1->a);
v2->a = 1.5;
printf("%lf\n", v2->a);
relmem_a(&v1);
relmem_b(&v2);
return 0;
}
-----------------------------------------
しかし、Cでやろうとするとやはりゴリ押し感は隠し切れないですね。
それと、トークン連結演算子(##)なるものがあることは知りませんでした・・・
やっぱ知ってるつもりで知らないことって結構あるものですねぇ
なるほど。
返信削除使いやすくするための改良ありがとうございます。
HOGEなどが定義されていない場合は関数自体定義されないようにしてある&自動的にundefするようにしたわけですね。