ポインタによる包含&ヘッダにincludeしない、場合でtemplateの定義に…

このQ&Aのポイント
  • クラスBのヘッダでクラスAのポインタを前方宣言していて、ソースファイルでクラスAの実装をincludeすることで、コンパイルエラーを回避できる。
  • テンプレート関数ではコンパイル時に解決が必要であるため、ヘッダに定義する必要がある。
  • 特殊化の場合、ソースファイルに定義できるが、これはC++の標準仕様ではなく、処理系に依存する。
回答を見る
  • ベストアンサー

ポインタによる包含&ヘッダにincludeしない、 場合でtemplateの定義に…

class Bの宣言をしているヘッダ中で class A; を、前方宣言し、そのポインタだけを持たせ、ソースファイルのほうにclass Aの中身が分かるように、#includeして、ソースファイルに関数の実装やstatic変数の定義を書いていた、とします。 しかし、templateを使う関数についてはコンパイル時に解決できないといけないので、それだけはヘッダに持ってきました。 その時 includeが一切書かれていない、class Bのヘッダ内において class Aのメンバを参照するようなコードを書いたとき クラス外、クラス内、いずれに書いても 正常にコンパイルできました。 通常の関数では当然無理なので、もともとtemplateがコンパイル時解決を強要するものなのでそういう仕様にしててくれてると考えられますが 1.これは、C++の標準仕様でしょうか?それとも処理系依存でしょうか? あと、templateに関して 2.特殊化ならソースにかけるのは標準仕様でしょうか?それとも処理系依存でしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

ざっと見た感じだけど, 上は 14.6.3節により OK な感じがする. 下の特殊化については必要なところにあればいいだけなので, ソースに書くことは可能です. ただし, ・すべてのソースで同じ特殊化が使われることをプログラムで保証しなければならない ・必要とする時点より前に特殊化を宣言しなければならない という条件があります (14.7.3節, パラグラフ 6). つまり, 現状のままではだめで, 特殊化した B::set<int> を B::ShowDouble の前に宣言しなければなりません. 節の番号は英語の規格における番号なので, 日本語のものだと違うかもしれません (日本語の規格は持ってないので不明).

LongSecret
質問者

お礼

お~ ありがとうございます♪ 下のコードは書いてる最中に色々いじったり部分的にコピペしたりしたので、確かにチェックしてみると順番入れ替わってたようですw 実際に下のとおりの順番にしてみると駄目でした。 あえて入れ替える場合はtemplate関数の特殊化も前方宣言をする必要があるということですね お手数お掛け致しました。 ちなみに普通に必要になった時より上で定義した場合は、この場合 template <> void B::Set<int>(int temp){/*…*/} と書かずとも template <> void B::Set(int temp){/*…*/} だけで判断できるので、<int>をつけなくても問題ないですよね? …そしてつまり、可能ではあるけどソース側に書くと、複数のソースで必要になった場合それぞれ必要なところで完全に同じものを何度も書かないといけないわけですか…?w 完全な隠蔽を実現して「どの道一つのソースでしか使わない状態」であればソースに書いてしまってもOKそうですが、そうでない場合は素直にヘッダに書いたほうがよさそうですね。 …という解釈でOKでしょうか? 規格っていうのは有料の規格書でしょうか?それともフリーの最終草案……ではやっぱないですよね?

LongSecret
質問者

補足

↓違う…w(実験後です) 丸々オーバーロードのように 特殊化した関数の「宣言」をヘッダにしておいて 定義は一か所でするといい感じ。 (ていうか定義を複数個所でやっちゃうとエラーになるので分かる…!(?)定義はあくまで一か所ですねw) もしそういうふうに、「宣言」をヘッダ側でやらない場合で、かつ複数のソースで使用する場合は、ソースごとに「宣言」を毎度書くしかない、と。 そして、宣言さえ適切に使用前に書いてやってれば普通の関数とまったく同じように特殊化した処理を使えるよ、ということですね? なるほど、そりゃそうかw

その他の回答 (2)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

今の場合は引数の型情報からテンプレートの型引数がわかるので template <> void B::Set(int temp){/*…*/} で十分です. もちろん Set<int> としても全く問題ありません. この場合は明示的な特殊化だから, その定義は「全プログラム中に 1つ」でいいのかな. あと, 日本語の規格については「日本工業標準調査会」ってところで調べれば見ることができます. 見るだけだけど. C++ なら X3014 で検索してください. 私が持ってるのは ISO から (安かったときに) 購入したやつなので全部英語ですが, 本当は日本語の規格を参照すべきなんだろうなぁ.

LongSecret
質問者

お礼

なるほど 最終草案とそんなに違わなそうですね(英語だから読みづらいと思ったら日本語でも十分読みづらい文章でしたw さすが規格書w そのかわりかなり詳しく書かれてる感じがしますけど) いずれにしても、templateに対して確実に理解が深まりました。 素晴らしい情報をありがとうございます♪

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

ん~, 状況がよくわかんない. どんなふうにソースを書いたんでしょうか?

LongSecret
質問者

お礼

ありがとうございます。実際のコードはかなり長いので、言いたいことを表すために、非常に簡略化したコード(例外とかアクセス指定とかも適当に)にしますが、それでもソースとヘッダが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 となり、動きとしては意図通りまったく問題なく機能しています。

LongSecret
質問者

補足

あ、最初double型で書こうとしてたので、関数名が謎になってますが、そこはスルーしてくださいw

関連するQ&A

  • ヘッダーファイルでは、他のヘッダーファイルをインクルードできないのか

    Javaを仕事で使っています。 最近趣味でC++を始めました。色々形式の違いに戸惑っています。 C++では関数を宣言しなければならないので、クラス名と同じヘッダーファイルにそのクラスで使う関数を宣言して、それをインクルードしています。 そこで今、壁にぶちあたりました。 あるクラス(仮にFooとします)の関数で、他のクラス型(Hogeとします)を引数に取りたいので、ヘッダーファイルにそれを宣言しようとしているのですが、コンパイルエラーになってしまいます。 Foo.hは以下のような感じ。 #include Hoge.h 中略 GetHoge(Hoge hoge); コンパイルエラーでは error C2011: 'Hoge' : 'class' 型の再定義 などと言われます。 クラスとその同名のヘッダーファイル、という形式を変えずに 他のクラス型を引数にとる関数を作るには、どうしたらよいのでしょうか? C++に関しては全くの素人です。詳しい方、ご教示願います。

  • ヘッダファイルのインクルードその2

    http://www.okweb.ne.jp/kotaeru.php3?q=541009 に関連した状況です。 ヘッダ同士がお互いにincludeしているとエラーになるそうですが、 異なる2つのclassがあって、互いにメンバ関数を呼び合うような場合、 includeはどのように行えばよいのでしょうか? 普通は、main.cppや、より上位のアプリケーション管理クラスに 書くものなのでしょうか? 試しにそうしてみたのですが、エラーを見ると、 何やらお互いに「そんなクラスは知らない」と言い合っているようなのです。 似たような質問で申し訳ないです。

  • なぜ、C++の標準ヘッダをインクルードするとき拡張子をつけないのか。

    なぜ、C++の標準ヘッダをインクルードするとき拡張子をつけないのか。 そもそもiostreamなどは拡張子をもっているのですか? 拡張子はあるけれどインクルードするときに付けてないだけとか?だとしたら、同じファイル名で拡張子の違いで判別できないですよね?a.hとa.hppがあったら#include <a>じゃわからないって意味。 自分でヘッダーファイルを作る場合はどのようにすればいいのでしょうか? 自分で作ったヘッダーファイルも標準ヘッダの様に拡張子を付けないでインクルードすることができますか?そういった事はするべきではないのでしょうか? この拡張子をつけないというのはC++の標準ヘッダだけ特別仕様とか? Cと同じように.hでヘッダーファイル作って#include "a.h"とかにしておけばいいのかなー?? 詳しい方、わかりやすく説明して頂けないでしょうか。よろしくお願いします。

  • ヘッダファイルを使わずに定義する方法

    新しい関数を使ったソースを古いOSでコンパイルすると、外部シンボル...Aが未解決になります。 ヘッダファイルでその関数のプロトタイプを書くとコンパイル可能になると思いますがヘッダファイルを使わずに*.cppにプロトタイプを書くようなことは可能ですか? __declspec(import) DWORD Win7OnlyEx(LPTSTR);

  • ヘッダーファイルのインクルード

    VC++6.0です。 ヘッダーファイルをincludeしていいかどうかは どのように決まるのでしょうか? classは、appとhogeの1種類があるとして、 app.h、app.cppとhoge.cpp、hoge.hの4つのファイルがあるとします。 1)appによるhogeのメンバの操作があって、その逆は無い場合 2)hogeもappのメンバを操作する場合 のそれぞれで教えてください。 最初、私は、そのclass内で別のclassのメンバ関数を 呼ぼうと呼ぶまいとincludeしてもかまわないと思っていました。 しかし、自分の作ったプログラムを検討したところ 1)の場合で、hogeのヘッダーに #include "app.h"と入れるとエラーになります。 hogeのcppファイルに#include "app.h"と入れてもエラーになります。

  • C++ヘッダの肥大化

    最近、テンプレートクラスとインライン関数を多用しているため、ヘッダファイルの肥大化が気になります。 ヘッダが肥大化するとコンパイル時間も大きくなり、更にヘッダファイルの可読性も落ちてしまうと思います。 また、ライブラリ化するときはコードを隠蔽したいのですが、ほとんどがヘッダファイルに実体があるため隠蔽できません。 インライン関数やテンプレートクラス、テンプレート関数は必ずヘッダファイルに書く必要があるのでしょうか。 標準ヘッダファイルでは「*.cc」という実体コードをインクルードしているので、それに習うべきなのでしょうか。

  • ヘッダーの中でヘッダーをインクルードすると?

    「自作ヘッダーAの中で別の自作ヘッダーBをインクルードする」 という事はどういう意味になり、どういう事が起こるのでしょうか? 「自作ヘッダーAの中で別の自作ヘッダーBをインクルードする」と 自作ヘッダーAの中で、 Bをインクルードした位置より下に定義した定数や構造体は 未定義とエラーが出てしまいます。 (は2回以上自作ヘッダーAをインクルードした時のみですが。) なぜこのような症状が現れるのでしょうか? >コンパイラ:VC++ 2008 EE

  • クラスのメンバ関数を別ファイルで定義したときのバグ

    C++ においてヘッダファイルで宣言したクラスのメンバ関数を別のソースファイルで定義して、コンパイルするとうまくいきません。エラーは出ないのですが、同名の何もしない関数としてコンパイルされているようなのです。クラスのメンバ関数を宣言したのと同じヘッダに書くとちゃんとコンパイルされます。 どうしてそうなるのか、いまいち原因がわかりません。

  • テンプレートクラス内のテンプレートクラス(インナークラス)のメソッドを実装ファイルで定義したい

    現在、ヘッダファイル内で下記のようなクラスを宣言・定義しています。 // test.h template < typename T1 > struct A {   template < typename T2 > struct B   {     B( A const& arg ) { ... }   }; }; テンプレートクラスが入れ子になっていて、Bのコンストラクタが引数としてAを取っています。 しかし現状ではコンパイルがとんでもなく遅くなってしまうので、 Bのコンストラクタは宣言のみとし、別途実装ファイル(test.cpp)に定義を書きたいと思っています。 ところがメソッドのシグネチャをどう書けばよいのか分からなくなってしまいました。 苦し紛れに // test.cpp template < typename T1, typename T2 > A<T1>::B<T2>::B( A<T1> const& arg ) { ... } などと書いてみましたが、違うようです。 解決方法はありますでしょうか? 環境はVC7.1かVC8でコンパイルできればよいです。 よろしくお願いいたします。

  • include無しにclassが書けない

    粗末な質問タイトルで申し訳ありません。 Visual C++2010で、includeを書かずにclass宣言を行おうとすると「Error:PCH警告」が発生します。 この理由をご教授ください。 IntelliSense: PCH 警告: 適切なヘッダー停止位置が見つかりません。 というエラーメッセージが表示されるのですが、これはプロトタイプ宣言がないときに発生する警告らしいですね。私が書こうとしているソースコードを次に示します。 -----main.cpp---- class TestClass {}; int main(void) { return (0); } ----------------- このソースで、"class"の部分に赤波線が引かれて先述のエラーメッセージが表示されます。 このソースの先頭に#include<iostream>なり#include<stdlib.h>なり何かインクルードを記述すると警告はなくなります。 しかしクラスはその枠のみですし、ライブラリ関数も何も使用していないのでとくに必要なインクルードファイルも何もないと思うのですが、どうしてこのようなエラーが発生するのでしょうか。 あと、蛇足になりますが、本来はクラス宣言部分を別のヘッダーファイルに分けたいのです。 クラス宣言部分を別のヘッダーに分離してヘッダーの先頭に #pragma once を記述するか、分離したヘッダをmain.cpp内でインクルードしてもエラーは解消されます。 (何からもインクルードされなかったらエラーになる) もう何が何やらさっぱりわからないです><

専門家に質問してみよう