marunomaruno-memo

marunomaruno-memo

[C++] テンプレートの特殊化

2011年11月19日 | C / C++
[C++] テンプレートの特殊化
================================================================================

テンプレートの指定で、Java のように、あるクラスを継承したクラスのみに限定する、
ということができる。
    Javaの例: class MyClass<T extends Number>

「テンプレートの特殊化」というものを使って実現できる。
でも、かなり面倒くさい。

まずは汎用的なテンプレート関数を宣言する。
    template<class T>                               // (4)
    void print(T& t) {

テンプレートの特殊化ということで、特定の型に対するテンプレート関数を宣言する。
    template<>                                      // (5)
    void print<Base> (Base& t) {

派生クラスのオブジェクト d を引数に渡すときは、基本クラスの型として渡す。
    print(*(dynamic_cast<Base*> (&d)));         // (10)

これで、派生クラスのオブジェクト d に対して、Base 型のテンプレート関数が使われる。

もちろん、単に
    print(d);                                   // (8)
とした場合は、汎用的なテンプレート関数が呼び出される。


□ generic_sample.cpp
---
#include <iostream>
using namespace std;

class Base {                                    // (1)
public:
    virtual void print() {
        cout << "Base" << endl;
    }
};

class Derived: public Base {                    // (2)
public:
    void print() {
        cout << "Derived" << endl;
    }
};

class Other {                                   // (3)
public:
    void print() {
        cout << "Other" << endl;
    }
};

template<class T>                               // (4)
void print(T& t) {
    cout << "G: ";
    t.print();
}

template<>                                      // (5)
void print<Base> (Base& t) {
    cout << "B: ";
    t.print();
}

//template<>                                    // (6)
//void print<Derived>(Derived& t) {
//    cout << "D: ";
//    t.print();
//}

int main() {
    Base b;
    print(b);                                   // (7)

    Derived d;
    print(d);                                   // (8)
    print(*((Base*) (&d)));                     // (9)
    print(*(dynamic_cast<Base*> (&d)));         // (10)

    Other o;
    print(o);                                   // (11)
//    print(*((Base*) (&o)));                   // (12) // 実行時エラー
//    print(*(dynamic_cast<Base*> (&o)));    // (13) // コンパイルエラー

    return 0;
}
---

□ 実行結果
---
B: Base                <- (7)
G: Derived             <- (8)
B: Derived             <- (9)
B: Derived             <- (10)
G: Other               <- (11)
---


(1) 基本クラス Base

    class Base {                                    // (1)
        ...
    };


(2) 派生クラス Derived

基本クラス Base を継承し、print() 関数をオーバーライドする。

    class Derived: public Base {                    // (2)
        ...
    };


(3) 関係ないクラス Other

Base クラスとも、Derived クラスとも関係のないクラス。

    class Other {                                   // (3)
        ...
    };


(4) テンプレート

汎用のテンプレート関数。引数は参照で受け取る。

    template<class T>                               // (4)
    void print(T& t) {
        ...
    }


(5) テンプレートの特殊化

Base 型で受け取るテンプレート関数。

    template<>                                      // (5)
    void print<Base> (Base& t) {
        ...
    }


(6) テンプレートの特殊化

Derived 型で受け取るテンプレート関数。今回は、コメントアウトしている。

    //template<>                                    // (6)
    //void print<Derived>(Derived& t) {
        ...
    //}

上記のコードを有効にしておくと、実行結果は次のようになる。

---
B: Base                <- (7) 
D: Derived             <- (8)
B: Derived             <- (9)
B: Derived             <- (10)
G: Other               <- (11)
---


(7) 基本クラスのオブジェクトをプリント

    print(b);                                   // (7)

特殊化によって、(5)のテンプレート関数が使われる。


(8)-(10) 派生クラスのオブジェクトをプリント

    print(d);                                   // (8)

特殊化は、あくまでも、そのクラス(型)が対象なので、その型の派生クラスというのは関
係ない。したがって、これは、あくまでも汎用のテンプレート関数を使う。


    print(*((Base*) (&d)));                     // (9)
    print(*(dynamic_cast<Base*> (&d)));         // (10)

派生クラスのオブジェクトを、基本クラスの型にキャストしてしまえば、基本クラスを使
った特殊テンプレートが利用できる。

キャストの方法としては、(9)のように、C言語のキャスト、dynamic_cast を使う 2 つの
方法があるが、dynamic_cast を使うほうがよい。これは、つぎの説明のように、基本ク
ラスとは派生関係にないクラスのオブジェクトをキャストして、特殊化したテンプレート
関数を使わせたいときに、C言語のキャストでは、実行時までエラーがわからないが、
dynamic_cast であれば、コンパイル・エラーになるからである。


(11) 関係ないクラスのオブジェクトをプリント

    print(o);                                   // (11)

汎用のテンプレート関数を使う。


//    print(*((Base*) (&o)));                   // (12) // 実行時エラー

このコードは、実行時にエラーになる。


//    print(*(dynamic_cast<Base*> (&o)));   // (13) // コンパイルエラー

このコードは、次のコンパイルエラーになる。
---
In function `int main()':
error: cannot dynamic_cast `&o' (of type `class Other*') to type `class Base
*' (source type is not polymorphic)
Build error occurred, build is stopped
---



最新の画像もっと見る

1 コメント

コメント日が  古い順  |   新しい順
Re: [C++] テンプレートの特殊化 (RiSK)
2011-11-28 17:09:17
はじめまして。RiSKと言います。

「あるクラスを継承したクラスのみに限定する」には,そのまんまの is_base_of というメタ関数を使うといいです。

またオーバーロードのOn/Offはenable_ifを使います。
# この辺はお決まりのイディオム

実際にコードを書くとこんな感じになります。
h ttps://ideone.com/uVbOG
# ↑https: にしてください

あと本題と関係ないのですが,
*constつける
クラスのメンバ関数およびグローバルな関数ともに。
constつけないと非const汚染が広がります。
*dynamic_castを極力使わない
Cスタイルキャストよりいいというのは正しいです。
実際Cスタイルキャストでは不可能な実行時のチェックができます。
しかし,それよりいいのはコンパイル時のチェックができるstatic_castです。
静的に型が定まるならば(これはほとんどのケース)static_castを使いましょう。

コメントを投稿