読者です 読者をやめる 読者になる 読者になる

templateのパラメータ引数に制限を設ける

templateはどんな型でも取れちゃうけど取れると困る場合がある。C++0xだとコンセプトっていうのがあるらしいけどC++0xなにそれおいしいの?なので…

実行してからじゃないとわからないようなのだとバグを仕込んでいるのと変わりないので、できるだけコンパイルエラーで検出したいですね。

型はなんでもいいけどスカラかポインタかは区別したい

参照で入れる場合などは注意ということでしょうな。

int main(void){
    int var;
    int* pVar;
    test(&var);
    test(&pVar);
}

class A{
public:
    template<class T> void test(T* p);
}

確かにこれは区別がつかないけど困るな。というわけで制限をかける。
スカラ以外はだめだよという場合は、

template<class T> void test(T* p){//実装する};
template<class T> static inline void test(T**); //実装しない

これは別にメンバ関数でもOK。template static...と一行で書くと怒られるのかな…(うろ覚え(あとで直します

逆にすからはだめで配列だとOKという場合はどうすればいいのだろう?あんまないからいいのか…?

型を制限したいその1(特殊化)

テンプレートの特殊化については以下を参照のこと。
http://www.geocities.jp/ky_webid/cpp/language/037.html
特殊化自体は「この型だけは別の実装で!」とするための手法(型がそんなに多くないならインターフェイスつくって継承先で実装した方がよさげだ)なんだけど、これを使って特定の型だけに制限をかける。

template<class T, class U> struct check_type;
template<class T, class U> void test(T var1, U* var2);
//許可する型の組
template<> struct check_type<int, char*>{};
template<> struct check_type<unsigned int, char*>{};

template<class T, class U> void test(T var1, U* var2){
    //型チェック
    (void)sizeof(check_type<T, U>);
    //実装
};

よく見てないでtest関数を特殊化しようとした馬鹿は僕だけで十分です。
TとUの組で知らないのが出てくるとどのcheck_typeかわからなくてエラーが出るんだねー。これである特定のだけはじけるとかできればいいのになぁ。
これは割と楽でいいんだけど、もっとたくさんある場合は全部書くの大変だし、継承関係あるのなら一気に調べたいという場合はその2へ。

型を制限したいその2
//テンプレートのパラメータ引数に入るはずのクラス
class A{};
class B{};
class A1{};
class B1{};
class A2: public A
{};
class B2: public B
{};

/* C.h */
/* テンプレートメンバ関数を持ったクラス */
#define STATIC_ASSERT(p) typedef int static_assertion_faild[ p ? 1 : -1]

typedef char correct_type;
typedef struct { char dummy2[]; } incorrect_type; //分かりやすいコンパイルエラーを出力

//正しい
correct_type type_check(const volatile A*, const volatile B*);
correct_type type_check(const volatile A1*, const volatile B1*);
//正しいもの以外は全部はじく
incorrect_type type_check(...)

class C
{
...
public:
	template<T, U> void func(T p1, U p2);
}

/* C.cpp */
template<T, U>
void func(T p1, U p2){
	STATIC_ASSERT((sizeof(type_check((T*)0, (U*)0)) == sizeof(correct_type));
	//あとの処理
}

void callFunc(){
    A a;
    B b;
    A1 a1;
    B1 b1;
    A2 a2;
    B2 b2;
    Adummy ad;
    Bdummy bd;

    //コンパイルエラーでない
    func(a, b);
    func(a1, b1);
    func(a2, b2);

  //コンパイルエラー出る
    func(ad, bd);
}

これはかっこいい!
これでcorrect_type以外の組み合わせの場合は弾いてくれる。volatileが付いてると継承クラスまでは許可するのかな。厳密にこれだけとするならvolatileはつけないほうがヨサゲ
http://proger.blog10.fc2.com/blog-entry-20.html
volatileについてはこちら。最適化を抑制するから子クラスからは継承元の公開されてるものは全部見えるので、同じものだよとわかってくれるっつーことか。

特に制限を書いてないけど制限できちゃうよという例

テンプレートパラメータが二つあるとできるぽい。

/* CMyList.h */
template<class T>
class CMyList{
public:
    void copyNum(Func1 func_){ m_func = func_; };
private:
    Func1 m_func;
};

/* Func.h */
class Func1 : public Func
{
public:
    &Func1 operator=(Func1 var){ 
        m_num = var.m_num;
        return *this; 
    };
private:
    int m_num;
}

/* CTest.h */
template<class T, class U> void test(CMyList<T>* list_, U fct);

/* CTest.cpp */
template<class T, class U>
void test(CMyList<T>* list_, U fct){
    list_->copy(fct);
}

/* main */
int main(void){
    CMyList<MyFactor> list;
    Func1 func1;
    Func func
    test(&list, func1);    //OK
    //test(&list, func);   //Compiler error
}

間にテンプレートクラスがあるからかどうかはまた別問題として、テンプレートはビルド時だったかコンパイル時に展開するために、展開先が見つからないとエラーを出してくれる(今までのも全部それでエラーが出る)ので、このTとUの組み合わせが常に決まっているような場合はエラーが出力されます。この場合だとMyListのコピーメソッドのところでFunc1と決定しているのでそれ以外のが来ちゃうとエラーが出る。
こんなバカみたいな設計になんねーからとか思うあなたは正しいと思います。
こういうのはそうなったからラッキーなのであって、基本的には自分でアサーション出させるとかにした方がいいんだろうなぁ。うーむ。