[解決済み] テンプレート化されたC++クラスを.hpp/.cppファイルに分割することは可能か?
質問
C++のテンプレートクラスをコンパイルしようとすると、エラーが発生します。
.hpp
と
.cpp
というファイルを作成します。
$ g++ -c -o main.o main.cpp
$ g++ -c -o stack.o stack.cpp
$ g++ -o main main.o stack.o
main.o: In function `main':
main.cpp:(.text+0xe): undefined reference to 'stack<int>::stack()'
main.cpp:(.text+0x1c): undefined reference to 'stack<int>::~stack()'
collect2: ld returned 1 exit status
make: *** [program] Error 1
以下は私のコードです。
stack.hpp :
#ifndef _STACK_HPP
#define _STACK_HPP
template <typename Type>
class stack {
public:
stack();
~stack();
};
#endif
スタック.cpp :
#include <iostream>
#include "stack.hpp"
template <typename Type> stack<Type>::stack() {
std::cerr << "Hello, stack " << this << "!" << std::endl;
}
template <typename Type> stack<Type>::~stack() {
std::cerr << "Goodbye, stack " << this << "." << std::endl;
}
メイン.cpp :
#include "stack.hpp"
int main() {
stack<int> s;
return 0;
}
ld
はもちろん正しいです。シンボルは
stack.o
.
の答えは
この質問
は、すでに書いてあるとおりにしているので、役に立ちません。
これは
は役に立つかもしれませんが、私はいちいちメソッドを
.hpp
ファイルに移動したくありません。
のすべてを移動させることが唯一の合理的な解決策でしょうか?
.cpp
ファイルから
.hpp
ファイルへリンクし、スタンドアロン オブジェクト ファイルとしてリンクするのではなく、単にすべてをインクルードするのですか?それは
非常に
醜い!それならいっそのこと、以前の状態に戻して、リネームして
stack.cpp
を
stack.hpp
に変更して終了です。
どのように解決するのですか?
テンプレートクラスの実装を別のcppファイルに記述してコンパイルすることはできません。もし誰かがそれを主張するなら、すべての方法は別の cpp ファイルの使用を模倣する回避策ですが、現実的には、テンプレート クラス ライブラリを書いて、実装を隠すためにヘッダーと lib ファイルを付けて配布しようとするなら、それは単に不可能です。
その理由を知るために、コンパイルの過程を見てみましょう。ヘッダーファイルは決してコンパイルされません。前処理が行われるだけです。前処理されたコードは、実際にコンパイルされる cpp ファイルと結合されます。さて、コンパイラがオブジェクトの適切なメモリレイアウトを生成しなければならない場合、テンプレートクラスのデータ型を知る必要があります。
実は、テンプレートクラスはクラスではなく、クラスのテンプレートであり、その宣言と定義はコンパイラが引数からデータ型の情報を得た後、コンパイル時に生成されることを理解する必要があります。メモリレイアウトが作成できない以上、メソッド定義のための命令も生成できません。クラスメソッドの第一引数は'this'演算子であることを覚えておいてください。すべてのクラスメソッドは、名前をマングリングして個々のメソッドに変換され、最初の引数は操作対象であるオブジェクトとなります。この引数はオブジェクトの大きさを表すもので、テンプレート・クラスの場合、ユーザーが有効な型引数でオブジェクトをインスタンス化しない限り、コンパイラはこれを利用することができません。この場合、メソッド定義を別のcppファイルに書いてコンパイルしようとすると、オブジェクトファイル自体がクラス情報を含んで生成されることはないでしょう。コンパイルは失敗せず、オブジェクト・ファイルは生成されますが、オブジェクト・ファイル内のテンプレート・クラスに対するコードは生成されません。これはリンカがオブジェクトファイル内のシンボルを見つけることができず、ビルドに失敗する理由です。
さて、重要な実装の詳細を隠すための代替案は何でしょうか?ご存知のように、インターフェイスと実装を分離する主な目的は、バイナリ形式で実装の詳細を隠すことです。そこで、データ構造とアルゴリズムを分離する必要があります。テンプレートクラスはデータ構造のみを表し、アルゴリズムは表さないようにします。これにより、より価値のある実装の詳細を、テンプレート化されていない別のクラス・ライブラリに隠すことができます。ライブラリ内のクラスは、テンプレート・クラス上で動作するか、データを保持するためだけに使用されます。テンプレート・クラスは、データの割り当て、取得、設定に必要なコードが少なくなります。残りの作業はアルゴリズムクラスによって行われるでしょう。
この議論が参考になれば幸いです。
関連
-
[解決済み] テスト
-
[解決済み】LLVMで暗黙のうちに削除されたコピーコンストラクタの呼び出し
-
[解決済み】Visual Studio 2015で「非標準の構文。'&'を使用してメンバーへのポインターを作成します」エラー
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み] string does not name a type Errorが発生するのはなぜですか?
-
[解決済み】C++の余分な資格エラー
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み】 while(cin) と while(cin >> num) の違いは何ですか?)
-
[解決済み] to_string は std のメンバーではない、と g++ が言っている (mingw)
-
[解決済み】Visual Studioのデバッガーエラー。プログラムを開始できません 指定されたファイルが見つかりません
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】識別子 "string "は未定義?
-
[解決済み】C++コンパイルタイムエラー:数値定数の前に期待される識別子
-
[解決済み] string does not name a type Errorが発生するのはなぜですか?
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み] 既に.objで定義されている-二重包含はない
-
[解決済み】Visual C++で "Debug Assertion failed "の原因となる行を見つける。
-
[解決済み】リンカーエラーです。"リンカ入力ファイルはリンクが行われていないため未使用"、そのファイル内の関数への未定義参照
-
[解決済み] gdbを使用してもデバッグシンボルが見つからない
-
[解決済み] なぜテンプレートはヘッダーファイルでしか実装できないのですか?
-
[解決済み] 明示的なテンプレートのインスタンス化 - どのような場合に使用するのですか?