明示的なインスタンス化の注意点

C++テンプレートテクニック 2-2


さて、以下のコードはコンパイルできるでしょうか?

#include <iostream>
using std::cout; using std::endl;

template<class T>
class CClass {
public:
    T m_val;
    CClass (T val) {
        m_val = val;
    }
    
    void Set (T val) {
        m_val = val;
    }
    
    T Get () {
        // sizeメソッドを呼び出す
        return m_val.size();
    }
};


int main () {
    // int型でインスタンス化
    CClass<int> obj(100);
    
    return 0;
}

一見するとGetメソッドにおいてm_valがオブジェクトを想定してsizeメソッドを呼んでいるため、int型でテンプレートのインスタンス化はできないように思えます。

しかし、テンプレートというのは実際に使われていないメソッドに関してはインスタンス化されない仕様なので、上記の処理は正しくコンパイルできます。

ただし、明示的なインスタンス化を行ってしまうと全てインスタンス化されてしまうのでコンパイルエラーになってしまいます。

// 明示的インスタンス化をする
template class CClass<int>;

int main () {
    CClass<int> obj(100);
    
    return 0;
}
    型は 'int' です。
    main.cpp(16): クラス テンプレート のメンバ関数 'int CClass<T>::Get(void)' のコンパイル中
    with
    [
        T=int
    ]
    main.cpp(22) : コンパイルされたクラスの テンプレート のインスタンス化 'CClass<T>' の参照を確認してください
    with
    [
        T=int
    ]

このような状況でも明示的なインスタンス化をしたいのであれば、使用するメソッドのみを明示的にインスタンス化させるということができるようです。

// Get以外のメソッドを明示的にインスタンス化
template CClass<int>::CClass(int);
template void CClass<int>::Set(int);

int main () {
    CClass<int> obj(100);
    
    return 0;
}

ただいちいち使用する全てのメソッドを列挙しないといけないので面倒ですね。

そもそもm_valがオブジェクトを期待してる状態なのにint型でインスタンス化させたいというのがクラスの設計ミスだと思います。