web-dev-qa-db-ja.com

C ++テンプレート、未定義の参照

私は次のように宣言された関数を持っています:

template <typename T> 
T read();

そしてそのように定義されます:

template <typename T>
T packetreader::read() {
    offset += sizeof(T);
    return *(T*)(buf+offset-sizeof(T)); 
}

ただし、main()関数で使用しようとすると、次のようになります。

packetreader reader;
reader.read<int>();

G ++から次のエラーが発生します。

g++ -o main main.o packet.o
main.o: In function `main':
main.cpp:(.text+0xcc): undefined reference to `int packetreader::read<int>()'
collect2: ld returned 1 exit status
make: *** [main] Error 1

誰かが私を正しい方向に向けることができますか?

28
Daniel Sloof

exportキーワードを使用する必要があります。ただし、G ++が適切にサポートされているとは思わないため、翻訳ユニットが使用できるように、テンプレート関数の定義をヘッダーに含める必要があります。これは、<int> 'バージョン'のテンプレートは作成されていません。<typename T> 'バージョン。'

簡単な方法は#include.cppファイル。ただし、これにより問題が発生する可能性があります。他の関数が.cppファイルにある場合。また、コンパイル時間が長くなる可能性があります。

クリーンな方法は、テンプレート関数を独自の.cppファイルに移動し、それをヘッダーに含めるか、exportキーワードを使用することです。個別にコンパイルします。

テンプレート関数の定義をヘッダーファイルに入れようとする理由の詳細(およびexportを完全に無視する)

24
strager

問題は、関数テンプレートが関数ではないことです。必要に応じて関数を作成するためのテンプレートです。

したがって、テンプレートが機能するためには、コンパイラーは直感的に2つの情報を必要とします。テンプレート自体とそれに置き換える必要のあるタイプです。これは、コンパイラが関数の存在を認識するとすぐに生成できる関数呼び出しとは異なります。関数が何をするのかを知る必要はありません。void Frobnicate(int, float)のように見えるか、そのシグネチャが何であれ、それだけです。

関数テンプレートを定義せずに宣言すると、そのようなテンプレートが存在することだけがコンパイラに通知され、どのように見えるかは通知されません。コンパイラがそれをインスタンス化できるようにするためにはそれだけでは不十分であり、完全な定義も見ることができなければなりません。通常の解決策は、必要に応じて含めることができるヘッダーにテンプレート全体を配置することです。

12
jalf

テンプレート関数のベストプラクティスは、ヘッダーファイルでそれらを定義することです。これらはコンパイル時に作成されるため、コンパイラーはそのために定義を持っている必要があります。

テンプレートのexportがよりサポートされる場合、これは当てはまりませんが、現時点ではまだほとんど使用できません。

4
vava

それらのコンパイラサポートテンプレートは個別にコンパイルされますか?

私が知っているように、一般的な方法は、ヘッダーファイルでテンプレート関数を宣言して実装することです

0
Baiyan Huang