2010-12-15 2 views
56

異なるオブジェクトファイルで特殊なテンプレートを使用すると、リンク時に「複数定義」エラーが発生します。私が見つけた唯一の解決策は、 "インライン"機能を使用することですが、ちょうどいくつかの回避策のように思えます。 「インライン」キーワードを使用せずにどのように解決できますか?それが不可能なら、なぜですか?異なるオブジェクトを使用する場合のテンプレート特殊化の複数定義

[email protected]:~/teste/cpp/redef$ cat hello.h 
#ifndef TEMPLATE_H 
#define TEMPLATE_H 

#include <iostream> 

template <class T> 
class Hello 
{ 
public: 
    void print_hello(T var); 
}; 

template <class T> 
void Hello<T>::print_hello(T var) 
{ 
    std::cout << "Hello generic function " << var << "\n"; 
} 

template <> //inline 
void Hello<int>::print_hello(int var) 
{ 
    std::cout << "Hello specialized function " << var << "\n"; 
} 

#endif 

[email protected]:~/teste/cpp/redef$ cat other.h 
#include <iostream> 

void other_func(); 

[email protected]:~/teste/cpp/redef$ cat other.c 
#include "other.h" 

#include "hello.h" 

void other_func() 
{ 
    Hello<char> hc; 
    Hello<int> hi; 

    hc.print_hello('a'); 
    hi.print_hello(1); 
} 

[email protected]:~/teste/cpp/redef$ cat main.c 
#include "hello.h" 

#include "other.h" 

int main() 
{ 
    Hello<char> hc; 
    Hello<int> hi; 

    hc.print_hello('a'); 
    hi.print_hello(1); 

    other_func(); 

    return 0; 
} 

:ここ

は、例えば、コードされ


最後に:

[email protected]:~/teste/cpp/redef$ make 
g++ -c other.c -o other.o -Wall -Wextra 
g++ main.c other.o -o main -Wall -Wextra 
other.o: In function `Hello<int>::print_hello(int)': 
other.c:(.text+0x0): multiple definition of `Hello<int>::print_hello(int)' 
/tmp/cc0dZS9l.o:main.c:(.text+0x0): first defined here 
collect2: ld returned 1 exit status 
make: ** [all] Erro 1 

私はhello.h内の「インライン」をコメント解除した場合は、コードをコンパイルして実行されますが、それはちょうど私には「回避策」のいくつかの種類のように思える:何特殊化された関数が大きくて何度も使用されていたら?大きなバイナリを入手できますか?これを行うための他の方法はありますか?はいの場合、どうですか?そうでない場合、なぜですか?

私は答えを探してみましたが、私が得たのはそれ以上の説明なしで「インラインで使う」ことでした。

おかげ

+5

ヘッダーファイル – Anycorn

答えて

82

直感的に言えば、何かを完全に特殊化しても、それはもはやテンプレートパラメータに依存しません。したがって、特殊化をインライン化しない限り、.hpか.cppファイルの代わりに.cppファイルに入れる必要がありますデビッドが言うように、1つの定義ルールに違反することになります。テンプレートを部分的に特殊化すると、部分的な特殊化は1つ以上のテンプレートパラメータに依存するため、依然として.hファイルになります。

+0

Hmmm私はODRをどのように破壊するかについてまだ混乱しています。一度完全に特殊化されたテンプレートだけを定義するからです。オブジェクトを別のオブジェクトファイルに複数回作成することもできます(この場合、other.cとmain.cでインスタンス化されます)。しかし、元のオブジェクト自体は1つのファイル(この場合は 'hello.h')でのみ定義されます。 。 –

+3

@JustinLiang:ヘッダーは、2つの別々の.cファイルに含まれています。これは、関連する場所にインクルードされているファイルにコンテンツ(完全な専門化を含む)を直接書き込んだ場合と同じ効果があります。 1つの定義ルール(http://en.wikipedia.org/wiki/One_Definition_Rule参照)には、(他のものの中でも)「プログラム全体で、オブジェクトまたは非インライン関数に複数の定義を含めることはできません。この場合、関数テンプレートの完全な特殊化は本質的に通常の関数のように行われるため、インラインでなければ複数の定義を持つことはできません。 –

+0

Hmmm、テンプレート化された特殊化がない場合、このエラーは表示されません。ヘッダーファイルに定義されている2つの異なる関数がクラスの外にあったとしても、インラインでは機能しないとしましょう。例:http://pastebin.com/raw.php?i=bRaiNC7M私はそのクラスを2つのファイルに含めました。これは2つのファイルに直接 "内容を書いたのと同じ効果"を持たないので、複数の定義エラーがありますか? –

30

キーワードinlineあるシンボルは、コンパイラが行うかしないかを決めることができ、実際のインライン化についてのより一つの定義ルールを違反することなく、複数のオブジェクトファイルに存在するであろうコンパイラに伝えるの詳細する。

あなたが見ている問題は、インラインなしでは、ヘッダーを含むすべての翻訳単位で関数がコンパイルされ、ODRに違反することです。 inlineを追加すると正しい方法があります。それ以外の場合は、他の機能と同様に、特殊化を宣言して単一の翻訳単位で提供することができます。

15

ヘッダーにテンプレートを明示的にインスタンス化しました(void Hello<T>::print_hello(T var))。これにより、複数の定義が作成されます。

1)インスタンス化をインラインで行います。

2)ヘッダーでインスタンス化を宣言し、それをcppで実装します。

+0

ではなく、実際の特殊な実装を.cppに入れます。実際には、名前のない名前空間にそれらを配置する3番目の方法があります。これはCで静的に似ています。 –

+2

ここでは無効です。テンプレートの特殊化は、元のテンプレートと同じ名前空間にある必要があります。 –