異なるオブジェクトを使用する場合のテンプレート特殊化の複数の定義


95

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

次にコード例を示します。

paulo@aeris:~/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

paulo@aeris:~/teste/cpp/redef$ cat other.h 
#include <iostream>

void other_func();

paulo@aeris:~/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);
}

paulo@aeris:~/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;
}

paulo@aeris:~/teste/cpp/redef$ cat Makefile
all:
    g++ -c other.c -o other.o -Wall -Wextra
    g++ main.c other.o -o main -Wall -Wextra

最後に:

paulo@aeris:~/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内の「インライン」のコメントを外すと、コードはコンパイルされて実行されますが、それはある種の「回避策」のように思えます。専用の関数が大きく、何度も使用されている場合はどうなりますか?大きなバイナリを入手できますか?これを行う他の方法はありますか?はいの場合、どのように?そうでない場合、なぜですか?

答えを探してみましたが、何も説明せずに「インラインで使う」だけでした。

ありがとう


6
実際の特殊な実装をヘッダーファイルではなく.cppに入れます
Anycorn

回答:


129

直感的には、何かを完全に特化しても、それはテンプレートパラメーターに依存しなくなります。そのため、特化をインライン化しない限り、.hの代わりに.cppファイルに配置する必要があります。そうしないと、 Davidが言うように1つの定義ルール。テンプレートを部分的に特化しても、部分的な特化は1つ以上のテンプレートパラメータに依存するため、.hファイルに含まれることに注意してください。


うーん私はまだそれがODRを壊す方法について少し混乱しています。完全に特殊化されたテンプレートを一度だけ定義するためです。異なるオブジェクトファイルでオブジェクトを複数回作成している可能性があります(つまり、この場合、other.cとmain.cでインスタンス化されています)が、元のオブジェクト自体は1つのファイルでのみ定義されています(この場合)hello.h
Justin Liang

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

うーん、テンプレート化された特殊化がない場合、このエラーは表示されないことに気付きました。ヘッダーファイルでクラスの外に定義された2つの異なる関数があるとしましょう。それらはインラインなしでも機能しますか?例:pastebin.com/raw.php?i=bRaiNC7M。私はそのクラスを取り、2つのファイルに含めました。これは「内容を書き込んだ場合と同じ効果」が2つのファイルに直接あるので、多重定義エラーが発生しませんか?
Justin Liang、

@Justin Liang、関数定義がクラスの本体内にない限り、複数のファイルに含まれている場合、クラスベースのヘッダーコードはODRに違反します。
haripkannan

それで、私の静的メンバー定義の前にあるtemplate <typename T>場合、それはヘッダーに入る可能性があり、そうである場合、そうでtemplate<>ない可能性がありますか?
バイオレットキリン2016年

49

キーワードinlineは、コンパイラーが実行するかしないかを決定できる実際のインライン化よりも、1つの定義規則に違反せずにシンボルが複数のオブジェクトファイルに存在することをコンパイラーに伝えることを目的としています。

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


22

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

1)インスタンス化をインラインにします。

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


実際に名前のない名前空間... Cで静的を持つに似ているのものを置くことである第三の方法はあり
アレクシス・ヴィルケ

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

0

ここではいくつかある作品、この問題に関連したC ++ 11標準のは:

関数テンプレートの明示的な特殊化は、インライン指定子で宣言されているか、削除済みとして定義されている場合にのみインラインであり、関数テンプレートがインラインであるかどうかとは無関係です。[例:

template void f(T){/ * ... /} template inline T g(T){/ ... * /}

template <> inline void f <>(int){/ * ... /} // OK:インラインtemplate <> int g <>(int){/ ... * /} // OK:インラインではない— end例]

そのため、*.hファイル内でテンプレートのいくつかの明示的な(別名:完全な)特殊化を行う場合でもinlineODRの違反を取り除く手助けをする必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.