- ベストアンサー
ポインタによる包含&ヘッダにincludeしない、場合でtemplateの定義に…
- クラスBのヘッダでクラスAのポインタを前方宣言していて、ソースファイルでクラスAの実装をincludeすることで、コンパイルエラーを回避できる。
- テンプレート関数ではコンパイル時に解決が必要であるため、ヘッダに定義する必要がある。
- 特殊化の場合、ソースファイルに定義できるが、これはC++の標準仕様ではなく、処理系に依存する。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
ざっと見た感じだけど, 上は 14.6.3節により OK な感じがする. 下の特殊化については必要なところにあればいいだけなので, ソースに書くことは可能です. ただし, ・すべてのソースで同じ特殊化が使われることをプログラムで保証しなければならない ・必要とする時点より前に特殊化を宣言しなければならない という条件があります (14.7.3節, パラグラフ 6). つまり, 現状のままではだめで, 特殊化した B::set<int> を B::ShowDouble の前に宣言しなければなりません. 節の番号は英語の規格における番号なので, 日本語のものだと違うかもしれません (日本語の規格は持ってないので不明).
その他の回答 (2)
- Tacosan
- ベストアンサー率23% (3656/15482)
今の場合は引数の型情報からテンプレートの型引数がわかるので template <> void B::Set(int temp){/*…*/} で十分です. もちろん Set<int> としても全く問題ありません. この場合は明示的な特殊化だから, その定義は「全プログラム中に 1つ」でいいのかな. あと, 日本語の規格については「日本工業標準調査会」ってところで調べれば見ることができます. 見るだけだけど. C++ なら X3014 で検索してください. 私が持ってるのは ISO から (安かったときに) 購入したやつなので全部英語ですが, 本当は日本語の規格を参照すべきなんだろうなぁ.
お礼
なるほど 最終草案とそんなに違わなそうですね(英語だから読みづらいと思ったら日本語でも十分読みづらい文章でしたw さすが規格書w そのかわりかなり詳しく書かれてる感じがしますけど) いずれにしても、templateに対して確実に理解が深まりました。 素晴らしい情報をありがとうございます♪
- Tacosan
- ベストアンサー率23% (3656/15482)
ん~, 状況がよくわかんない. どんなふうにソースを書いたんでしょうか?
お礼
ありがとうございます。実際のコードはかなり長いので、言いたいことを表すために、非常に簡略化したコード(例外とかアクセス指定とかも適当に)にしますが、それでもソースとヘッダが2つずつになるので多少煩雑かもしれません。ご容赦ください。 ////////////Aのヘッダ(A.h)をこういうものとします。//////////// #pragma once class A{ public: A(int); ~A(void); int data; }; ////////////Aのソース(A.cpp)をこう仮定します。//////////// #include "StdAfx.h" //これはプリコンパイル済みヘッダーです #include "A.h" A::A(int i) : data(i){} A::~A(void){} ////////////この状態で、クラスBのヘッダをこうします。//////////// #pragma once class A; class B{ A* a; template <class T> void Set(T temp){ a->data=(int)temp; }; //↑これが処理系依存かどうかが疑問1です。 public: B(void); ~B(void); void ShowDouble(); }; ////////////Bのソースにて、確認処理を作ってみます//////////// #include "StdAfx.h" #include "A.h" #include "B.h" B::B(void) : a(0) { a=new A(1); } B::~B(void){ delete a; } void B::ShowDouble(){ char c[20]; sprintf_s(c,20,"%d",a->data); ::MessageBox(0,c,"",0); Set(32.43278); sprintf_s(c,20,"%d",a->data); ::MessageBox(0,c,"",0); Set(7); sprintf_s(c,20,"%d",a->data); ::MessageBox(0,c,"",0); } template <> void B::Set<int>(int temp){ a->data=temp; ::MessageBox(0,"int型で呼ばれました","",0); }; //↑これがソース側にかけることが処理系依存かどうかが第2の疑問です。 これで、初めてShowDouble()を呼び出したとき メッセージボックスが4回呼ばれます。 結果は 1 32 int型で呼ばれました 7 となり、動きとしては意図通りまったく問題なく機能しています。
補足
あ、最初double型で書こうとしてたので、関数名が謎になってますが、そこはスルーしてくださいw
お礼
お~ ありがとうございます♪ 下のコードは書いてる最中に色々いじったり部分的にコピペしたりしたので、確かにチェックしてみると順番入れ替わってたようですw 実際に下のとおりの順番にしてみると駄目でした。 あえて入れ替える場合はtemplate関数の特殊化も前方宣言をする必要があるということですね お手数お掛け致しました。 ちなみに普通に必要になった時より上で定義した場合は、この場合 template <> void B::Set<int>(int temp){/*…*/} と書かずとも template <> void B::Set(int temp){/*…*/} だけで判断できるので、<int>をつけなくても問題ないですよね? …そしてつまり、可能ではあるけどソース側に書くと、複数のソースで必要になった場合それぞれ必要なところで完全に同じものを何度も書かないといけないわけですか…?w 完全な隠蔽を実現して「どの道一つのソースでしか使わない状態」であればソースに書いてしまってもOKそうですが、そうでない場合は素直にヘッダに書いたほうがよさそうですね。 …という解釈でOKでしょうか? 規格っていうのは有料の規格書でしょうか?それともフリーの最終草案……ではやっぱないですよね?
補足
↓違う…w(実験後です) 丸々オーバーロードのように 特殊化した関数の「宣言」をヘッダにしておいて 定義は一か所でするといい感じ。 (ていうか定義を複数個所でやっちゃうとエラーになるので分かる…!(?)定義はあくまで一か所ですねw) もしそういうふうに、「宣言」をヘッダ側でやらない場合で、かつ複数のソースで使用する場合は、ソースごとに「宣言」を毎度書くしかない、と。 そして、宣言さえ適切に使用前に書いてやってれば普通の関数とまったく同じように特殊化した処理を使えるよ、ということですね? なるほど、そりゃそうかw