モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

工作HardwareHub ロゴ画像 (Laptop端末利用時)
工作HardwareHub ロゴ画像 (Mobile端末利用時)

クラステンプレート (C++をもう一度)

モーダルを閉じる

ステッカーを選択してください

モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2014/12/10
最終更新最終更新
2021/10/07
記事区分記事区分
一般公開

目次

    低レイヤーのプログラミングとOS開発が趣味。C言語を使っています。

    0
    ステッカーを贈るとは?

    サンプルコード

    my_class.h

    #ifndef MY_CLASS_H_
    #define MY_CLASS_H_
    
    // 関数テンプレート と同様、
    // テンプレートは通常ヘッダファイルにすべてを記述する必要があります。
    // ヘッダファイルでの using 使用は好ましくないため std::cout 等とします。
    
    #include <iostream>
    
    template <typename MY_TYPE>
      class MyClass
    {
     public:
        MyClass(MY_TYPE val);
        MyClass(const MyClass& other); // コピーコンストラクタ
    
     public:
        void Say() const;
    
     private:
        MY_TYPE m_val;
    };
    
    template <typename MY_TYPE>
      MyClass<MY_TYPE>::MyClass(MY_TYPE val) :
        m_val(val)
    {
        // m_val = val;  // とするよりもシンプル
    }
    
    template <typename MY_TYPE>
      MyClass<MY_TYPE>::MyClass(const MyClass& other)
      // MyClass<MY_TYPE>::MyClass(const MyClass<MY_TYPE>& other)  // の省略形
      // ↑コンパイラが古くなければ「MyClass<MY_TYPE>」以降の「MyClass」は
      // 「MyClass<MY_TYPE>」と解釈されます。
    {
        // コピーするだけの例
        m_val = other.m_val;
    }
    
    template <typename MY_TYPE>
      void MyClass<MY_TYPE>::Say() const
    {
        std::cout << "MyClass::Say, " << m_val << std::endl;
    }
    
    #endif // #ifndef MY_CLASS_H_
    

    main.cpp

    #include <iostream>
    #include "my_class.h"
    using namespace std;
    
    int main() {
    
        // 関数テンプレートと異なり、型の自動判別は困難な場合
        // が多く、無効化されています。明記する必要があります。
        MyClass<int> obj(1);
        MyClass<int> objCopied = obj;
        obj.Say();
        objCopied.Say();
    
        return 0;
    }
    

    デフォルトテンプレート引数

    関数テンプレートとは異なり、クラステンプレートにはテンプレート引数の既定値を記述できます。

    my_class.h

    #ifndef MY_CLASS_H_
    #define MY_CLASS_H_
    
    #include <iostream>
    
    template <typename MY_TYPE = char>
      class MyClass
    {
     public:
        MyClass(MY_TYPE val);
    
     public:
        void Say() const;
    
     private:
        MY_TYPE m_val;
    };
    
    template <typename MY_TYPE>
      MyClass<MY_TYPE>::MyClass(MY_TYPE val) :
        m_val(val)
    {
    }
    
    template <typename MY_TYPE>
      void MyClass<MY_TYPE>::Say() const
    {
        std::cout << "MyClass::Say, " << m_val << std::endl;
    }
    
    #endif // #ifndef MY_CLASS_H_
    

    main.cpp

    #include <iostream>
    #include "my_class.h"
    using namespace std;
    
    int main() {
        MyClass<> ch(97);
        MyClass<int> i(97);
        ch.Say(); //=> MyClass::Say, a
        i.Say(); //=> MyClass::Say, 97
        return 0;
    }
    

    なお、デフォルトテンプレート引数が複数ある場合は以下のように既出のテンプレート引数の値に依存させることができます。

    template <typename MY_TYPE = char, typename MY_TYPE_2 = MY_TYPE>
    template < typename MY_TYPE = char, typename MY_TYPE_2 = MyOtherClassTemplate<MY_TYPE> >
    

    その際、後者の例では ">" と ">" の間にスペースを入れて、コンパイラが演算子 ">>" と区別できる状態にする必要があります。

    テンプレート引数にクラステンプレート

    my_class.h

    #ifndef MY_CLASS_H_
    #define MY_CLASS_H_
    
    #include <iostream>
    
    template <template <typename, typename> class MY_TEMPL>
      class MyClass
    {
     public:
        void SetShow();
    
     private:
        MY_TEMPL< int, std::allocator<int> > m_val;
    };
    
    template <template <typename, typename> class MY_TEMPL>
      void MyClass<MY_TEMPL>::SetShow()
    {
        m_val.push_back(0);
        m_val.push_back(1);
        std::cout << "MyClass::SetShow(" << m_val.size() << ")" << std::endl;
    }
    
    #endif // #ifndef MY_CLASS_H_
    

    main.cpp

    #include "my_class.h"
    #include <iostream>
    #include <vector>
    #include <deque>
    using namespace std;
    
    int main() {
        MyClass<vector> objV;
        MyClass<deque> objD;
    
        objV.SetShow(); //=> MyClass::SetShow(2)
        objD.SetShow(); //=> MyClass::SetShow(2)
    
        objV.SetShow(); //=> MyClass::SetShow(4)
        objD.SetShow(); //=> MyClass::SetShow(4)
    
        return 0;
    }
    

    テンプレート引数に整数

    my_class.h

    #ifndef MY_CLASS_H_
    #define MY_CLASS_H_
    
    #include <iostream>
    #include <algorithm>
    
    template <int N> // double など小数は指定できない仕様 (環境によってはコンパイルできてしまう)
      class MyClass
    {
     public:
        static const int SIZE = N; // 静的メンバ定数
    
     public:
        MyClass();
    
     public:
        void Show(int i) const;
    
     private:
        int m_intarr[SIZE];
    };
    
    template <int N>
      MyClass<N>::MyClass()
    {
        std::fill_n(m_intarr, SIZE, 0);
    }
    
    template <int N>
      void MyClass<N>::Show(int i) const
    {
        std::cout << m_intarr[i] << std::endl;
    }
    
    #endif // #ifndef MY_CLASS_H_
    

    main.cpp

    #include "my_class.h"
    #include <iostream>
    using namespace std;
    
    int main() {
        MyClass<8> obj;
    
        for (size_t i = 0, size = MyClass<8>::SIZE; i < size; ++i) {
            obj.Show(i);
        }
    
        cout << MyClass<8>::SIZE << endl; //=> 8
        cout << MyClass<16>::SIZE << endl; //=> 16
    
        return 0;
    }
    

    特殊化

    テンプレート引数が特定のパターンに合致した場合のために、特殊なテンプレートを用意しておくことができます。これをクラステンプレートの特殊化とよびます。特に、テンプレート引数の一部のみを固定して用意する場合を部分特殊化とよびます。なお、特殊なテンプレートを用意する方法としては、一部メンバのみを用意しておく方法 (一部上書き) とクラステンプレート全体を用意しておく方法 (すべて上書き) の二つがあります。

    my_class.h

    #ifndef MY_CLASS_H_
    #define MY_CLASS_H_
    
    #include <iostream>
    #include <utility> // pairのため
    
    template <typename MY_TYPE_1, typename MY_TYPE_2>
      class MyClass
    {
     public:
        MyClass(MY_TYPE_1 val1, MY_TYPE_2 val2);
    
     public:
        void Show() const;
    
     private:
        MY_TYPE_1 m_val1;
        MY_TYPE_2 m_val2;
    };
    
    template <typename MY_TYPE_1, typename MY_TYPE_2>
      MyClass<MY_TYPE_1, MY_TYPE_2>::MyClass(MY_TYPE_1 val1, MY_TYPE_2 val2) :
        m_val1(val1),
        m_val2(val2)
    {
    }
    
    template <typename MY_TYPE_1, typename MY_TYPE_2>
      void MyClass<MY_TYPE_1, MY_TYPE_2>::Show() const
    {
        std::cout << m_val1 << ", " << m_val2 << std::endl;
    }
    
    // 完全な特殊化 (一部上書き)
    template <>
      void MyClass<double, double>::Show() const
    {
        std::cout << "double double: " << m_val1 << ", " << m_val2 << std::endl;
    }
    
    // 完全な特殊化 (すべて上書き)
    template <>
      class MyClass<char, char>
    {
     public:
         MyClass(char val1, char val2) : m_val1(val1), m_val2(val2) {}
    
     public:
        void Show() const {
            std::cout << "char char: " << m_val1 << ", " << m_val2 << std::endl;
        }
    
     private:
        char m_val1;
        char m_val2;
    };
    
    // 部分特殊化 (一部上書き) → 実装できません
    
    // 部分特殊化 (すべて上書き)
    template <typename MY_TYPE_2>
      class MyClass<char, MY_TYPE_2>
    {
     public:
         MyClass(char val1, MY_TYPE_2 val2) : m_val1(val1), m_val2(val2) {}
    
     public:
        void Show() const {
            std::cout << "char *: " << m_val1 << ", " << m_val2 << std::endl;
        }
    
     private:
        char m_val1;
        MY_TYPE_2 m_val2;
    };
    
    // 部分特殊化 (すべて上書き。複雑な例)
    template <typename MY_TYPE_2, typename FIRST, typename SECOND>
      class MyClass<std::pair<FIRST, SECOND>, MY_TYPE_2>
    {
     public:
        MyClass(FIRST val1_f, SECOND val1_s, MY_TYPE_2 val2) : m_val1(val1_f, val1_s), m_val2(val2) {}
    
     public:
        void Show() const {
            std::cout << "pair *: ("
                      << m_val1.first << ", " << m_val1.second << "), "
                      << m_val2 << std::endl;
        }
    
     private:
        std::pair<FIRST, SECOND> m_val1;
        MY_TYPE_2 m_val2;
    };
    
    #endif // #ifndef MY_CLASS_H_
    

    main.cpp

    #include "my_class.h"
    #include <iostream>
    using namespace std;
    
    int main() {
        MyClass<int,int> objII(1, 1);
        MyClass<double,double> objDD(1.1, 1.1);
        MyClass<char,char> objCC(97, 97);
        MyClass<char,int> objCI(97, 1);
        MyClass<pair<int,int>,int> objPI(1, 1, 1);
    
        objII.Show(); //=> 1, 1 (通常のテンプレート)
        objDD.Show(); //=> double double: 1.1, 1.1 (特殊化)
        objCC.Show(); //=> char char: a, a (特殊化) ← 部分特殊化したものよりも特殊化したものが優先的に利用されます。
        objCI.Show(); //=> char *: a, 1 (部分特殊化)
        objPI.Show(); //=> pair *: (1, 1), 1 (部分特殊化)
    
        return 0;
    }
    
    0
    詳細設定を開く/閉じる
    アカウント プロフィール画像 (本文下)

    低レイヤーのプログラミングとOS開発が趣味。C言語を使っています。

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    さらに詳しく →
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      関連記事