ややプログラム紀行

博士1年のプログラムに関する日記

テンプレートを継承

最近全然更新できてないっすね…まあ冬休みだからしゃーない(諦め



あまり書くような内容もないんですね実は

ブラインドタッチもどきができるようになったってことくらいでしょうか(今までできなかった

そういえば最近Rustを少し触っているのでもう少し慣れたらRust関連の記事も書いてみたいなと思います



せっかくなのでこの前遭遇したちょっとした小ネタでも書こうと思います

以前、ちょっとシングルヘッダーライブラリというものを作ってみようと思い立ち、学校で作ったSchemeの処理系をシングルヘッダーライブラリ & C++11にしてみることにしました

qiita.com

シングルヘッダーライブラリというのは少し慎重に書かないと、異なるファイルからインクルードした時に多重定義に引っかかってポシャるんですね

それを回避するためには、関数をインライン関数にしてしまうか、テンプレートクラスを用いるなどの方法があるようです

テンプレートが回避できる理由としては、C++のテンプレートは具体的に型を当てはめた時点で初めてインスタンス化されるので、シングルヘッダーライブラリを何回読み込んでも多重定義にはならず、インスタンス化された地点でコンパイラがなんとかしてくれるっていうからくりがあります(多分


僕は何としてでもクラスを使いたかったのでテンプレートを利用して多重定義を回避することにしたんですが、いざ書いてみると次のような感じになりました

#include <iostream>

using namespace std;

template <class T>
class Base {
protected:
    T x;
};

template <class T>
class Derived : Base<T> {
public:
    void print(T y) {
        x = y;
        cout<<x<<endl;
    }
};

int main() {
    Derived<int> d;
    d.print(10);

    return 0;
}

これをコンパイルしようとすると怒られます😭

main.cpp:15:3: error: use of undeclared identifier 'x'
                x = y;
                ^
main.cpp:16:9: error: use of undeclared identifier 'x'
                cout<<x<<endl;
                      ^
2 errors generated.

これ知らない人はマジでハマると思うんですけど、さっき言ったテンプレートのインスタンス化が関係してるエラーなんですね
Baseクラスがテンプレートなのでコンパイル時点ではxという変数がなんなのかまだ分かっていないので上のようなエラーがでるんだと思います
というかここまで書いたところで不安になってググってみたところ、全く同じ内容の記事を見つけました😇

qiita.com

まぁ、そういうわけです(投げやり

ちなみに上の記事ではthisを用いてこのエラーを回避していますが、

template <class T>
class Derived : Base<T> {
public:
    void print(T y) {
        Base<T>::x = y;
        cout<<Base<T>::x<<endl;
    }
};

なんて書き方しても回避できます、thisの方が短くていいですね



追記:
貼った記事と微妙に状況が違う気がするので軽く説明すると、記事にあるようにテンプレートにはTwo Phase Look-upと呼ばれる名前解決の決まりがあり、まずテンプレートに依存しないものの名前解決、そのあとにテンプレートに依存するものの解決という順序になっているんですね
上のサンプルコードの場合、まずBaseクラスの定義を見ると変数xはテンプレートに依存するので後回し、次にDerivedクラスを見るとxというものがいきなり出てきてなんだこいつ?!となるというわけだと思います

にしても貼った記事と僕のサンプルコードめちゃくちゃ似てますね