私は今、テンプレートクラスの継承クラスをライブラリ化して、
別のプロジェクトから参照するプログラムを作っています。
template <typename T> class A
{
インターフェース
};
class B:public A<Tm>
{
インターフェース
};
だいたいこのような感じでプログラムを組んで、別のプロジェクトでヘッダーをインク
ルードして、VCの依存関係の設定でこのクラスを含むプロジェクトを設定しました。
しかし、このクラスをライブラリ外の別のプロジェクトで利用すると
class A に関すると思われる、外部参照は未解決ですのエラーが大量に発生します。
ライブラリ内部で呼び出す場合はコンパイルエラーは出ません。
この外部参照は未解決です。エラーを解決する方法をご存知の方、
ご教授よろしくお願いします。
環境
OS WindowsXP
コンパイラ VisualStudio .NET 2003 Pro
プロジェクト MFCプロジェクト
できません。諦めよう。
クラステンプレートはクラスのテンプレート(雛形)であって、クラスではありませ
ん。
例えば、std::vector はクラスではありません。std::vector< int > はクラスです。
このように、クラステンプレートに具体的な型引数を与えてクラス化することを、「テ
ンプレートのインスタンス化」と言います。
今回の例で言えば、A に渡す型引数が、ライブラリのコンパイル時にわからないため、
クラステンプレート A はインスタンス化されません。
平たく言うならば、コンパイルされたライブラリの中に、クラス A はありません。
ライブラリを使う側は、インスタンス化された A を要求しますが、そんなもんないの
で、リンクに失敗します。
ライブラリファイルをパスの通っているフォルダもしくは、カレントフォルダに置かれ
ていますか?
もしくは、LoadLibrary()等を行って、動的にライブラリをリンクしていますか?
ヘッダファイルをカレントディレクトリに置かれているか、インクルードする際に、そ
こへのフルパスを指定していますか?
あ、ちょっと趣旨がずれてたみたいで申し訳ないです。。。
明示的にインスタンス化してもダメですか?
ライブラリ側のソースに
template class A<ライブラリの外で使う型>;
とかってやってみてだめならあきらめませう。
シャノン さん。DDさん。ご返答ありがとうございました。
結論が出てよかったです。
> 明示的にインスタンス化してもダメですか?
それならできると思われますが、ライブラリの汎用性が落ちてしまいます。
トレードオフですね。
ん?
Aの実装をヘッダに書いてないだけじゃなくて?
> Aの実装をヘッダに書いてないだけじゃなくて?
ライブラリとのことなので、場合によっては
実装を外から見えないようにしたいこともあります。
この場合はソースに実装を書いて明示的にインスタンス化するんですが、
それだとシャノンさんの指摘しているように
> ライブラリの汎用性が落ちてしまいます。
ということになります。
必要な分を全部明示的にインスタンス化するのも
いかがなものかとも思いますので
そこはトレードオフってことですね。
結局、
1) テンプレートの実装をヘッダに書いて、汎用性を確保する。
2) 汎用性を捨てて、必要な実体のみをライブラリ内でインスタンス化する。
のいずれかで実現可能なわけですよね?
あるいは、基本的に2)のスタンスを取りつつ、
ライブラリ側でクラステンプレートAの実装のみを記述したヘッダを公開し、
アプリケーション側でAの別のインスタンスが必要なら
そのヘッダをインクルードさせれば、汎用性も確保できるのでは。
「実装の詳細内容を(ライブラリ使用者に)見せたくない」という要求と
「テンプレートとしての汎用性を確保したい」という要求は
完全に相反するので、その両立はできないというのならわかります。
> ライブラリとのことなので、場合によっては
> 実装を外から見えないようにしたいこともあります。
元発言者がその「場合」なのだとは判断できなかったもので。
私の中では、テンプレートの実装はヘッダに書くものだという意識が
固定観念化されてしまってたので、あんな書き方になったのでした。
> 必要な分を全部明示的にインスタンス化するのも
> いかがなものかとも思いますので
ライブラリ内に閉じた話なので何の問題もないような・・・?
いずれにせよ、「できません」といきなり断言するのはいかがなものかと。
> クラステンプレートAの実装のみを記述したヘッダを公開し、
そもそもテンプレートクラスの実装を公開したくないから
ヘッダーに実装を書いてないんじゃないでしょうか??
それとも単にテンプレートが展開されるメカニズムを元発言者が
理解してなかったのでしょうか??
この辺は元発言者にしかわからないですね orz
私も自分の固定観念の上で発言してしまった模様です。
気を悪くされたのであれば申し訳ありません。
> 元発言者がその「場合」なのだとは判断できなかったもので。
私は断定したつもりはなかったのですが、
確かにそのように受け取れる文章でした。
> ライブラリ内に閉じた話なので何の問題もないような
明示的インスタンス化を行う場合、
別の型を指定したテンプレートの実装が必要になった際に
毎回ライブラリの変更が必要になってしまいます。
こうなるとライブラリとしての汎用性が損なわれてしまいます。
また、テンプレートクラスを外部に公開する以上は
「ライブラリ内に閉じた話」とは言えないのではないでしょうか?
>いずれにせよ、「できません」といきなり断言するのはいかがなものかと。
「できません」といきなり断言することも、
「これならできます。」と代案を出すのも
僕は、両方正しいと思います。
「できません」と断言したのは
「実装を外から見えないようにしたい」
ということを重視しただけだと思います。
なっくんさんが、テンプレートを「vector」や「std」などの便利な機能
を使いたいので使用したというのなら、
「これならできます。」と代案を出すほうがいいと思います。
>kureさん
> 別の型を指定したテンプレートの実装が必要になった際に
> 毎回ライブラリの変更が必要になってしまいます。
ちょっと言葉足らずでした。
2)の選択肢、つまり「テンプレートの実装を公開しない」という設計を選択した場合、
ライブラリユーザはライブラリ定義外のインスタンス化はできないという
制限を課すのは必然になるわけで。
つまりそういう設計上のトレードオフを選択した以上は、
明示的なインスタンス化はライブラリに閉じた話になりますよね。
>ITOさん
> 「できません」といきなり断言することも、
> 「これならできます。」と代案を出すのも
> 僕は、両方正しいと思います。
うーむ。
結局できないことはないわけだし、
それを「できない」ということが正しいとは思えないんだけどなあ。
ていうか、どうやら元発言者の書き込みから
「テンプレートの実装は公開したくない」という
言外の意図を読み取っている方が大多数のようですね。
Aの宣言内に「インターフェース」とあるからなのかな。
#読解力不足だなぁ>私
なので、私のツッコミは余計なものだったのかも。失礼しました。
>なので、私のツッコミは余計なものだったのかも。失礼しました。
今回は「テンプレートの実装は公開したくない」という方に軍配
があがりましたが、いつもそうなるとは思いません。
「ツッコミ」によってなっくんさんから「こんな方法があるんですかぜひ
教えてください。」という発言があったかも知れません。
テンプレートは「関数の実装を公開しないようにする」ためだけにあるのでは
ないと思います。
逆にいうと、テンプレートは、「関数の実装を公開しないようにすること」
ができるということを誰もが知っているわけではないと思います。
中には、テンプレートの便利な機能、「配列、vecter、list等」だけを
使う人もいると思います。
> 今回は「テンプレートの実装は公開したくない」という方に軍配
> があがりましたが、いつもそうなるとは思いません。
なっくん さんの明確な回答があるわけでもないですし、
「軍配」なんてあがってないと思いますが。
単にそうとった人の書き込みがちょっと多いだけでしょう。
> テンプレートは「関数の実装を公開しないようにする」ためだけにあるのでは
> ないと思います。
「だけ」というか、テンプレートに「実装を公開しないようにするための機能」
なんてそもそも「実装されてないない」ような…。
■チラシの裏
私の当初の認識も「実装を隠匿したい」という意図は感じてませんでした。
> ライブラリ内部で呼び出す場合はコンパイルエラーは出ません。
> この外部参照は未解決です。エラーを解決する方法をご存知の方、
> ご教授よろしくお願いします。
テンプレートとかに依存した話ではなく(たまたまclassAで現象が出ただけ)、
・「この外部参照は未解決です。」はリンクエラー
・(静的)ライブラリだと(リンクしないから)外部参照エラーが出ない。
・外のプログラムとリンクしたらエラーがいっぱい出た
と書かれているだけかと思ってましたし…。