13

1つのメソッドを持つヘッダーで宣言するテンプレートクラスがあり、ヘッダーにそのメソッドの定義はありません。.ccファイルでは、ヘッダーで宣言せずに、そのメソッドの特殊化を定義します。別の.ccファイルで、特殊化が存在するさまざまなテンプレートパラメーターのメソッドを呼び出します。次のようになります。

foo.h:

template<typename T>
class Foo {
public:
  static int bar();
};

foo.cc:

#include "foo.h"

template<>
int Foo<int>::bar() {
  return 1;
}

template<>
int Foo<double>::bar() {
  return 2;
}

main.cc:

#include <iostream>
#include "foo.h"

int main(int argc, char **argv) {
  std::cout << Foo<int>::bar() << std::endl;
  std::cout << Foo<double>::bar() << std::endl;
  return 0;
}

このプログラムは、すべてのC ++標準(c ++ 98、gnu ++ 98、c ++ 11、およびgnu ++ 11)のgcc 4.7.2で正常にコンパイルされ、リンクされます。出力は次のとおりです。

1
2

これは私には理にかなっています。main.cc変換ユニットは、その定義bar()または特殊化を認識しないため、他の変換ユニットbar()での特殊化されていない定義の明示的なインスタンス化を使用する呼び出しを想定していますbar()。ただし、名前マングリングは予測可能であるため、foo.ccの特殊化は、特殊化されていない定義の明示的なインスタンス化と同じシンボル名を持ちます。したがって、main.ccは、その変換単位で宣言されることなく、これらの特殊化を使用できます。

私の質問はこれです:これは事故ですか、それともこの動作はC ++標準で義務付けられていますか?言い換えれば、このコードは移植可能ですか?

私が見つけた最も関連性のある以前の質問は、テンプレートクラスメンバーの特殊化の宣言ですが、この特定のケースについては説明していません。

(これがなぜ私にとって重要なのか疑問に思われるかもしれませんが、これは、このようなコードを一種のコンパイル時ルックアップテーブルとして使用しているためです。特殊化を宣言しないと、はるかに短くなります。)

4

1 に答える 1

9

標準 (C++11) では、明示的な特殊化を最初に使用する前に宣言する必要があります (必ずしも定義する必要はありません)。

(14.7.3/6) テンプレート、メンバー テンプレート、またはクラス テンプレートのメンバーが明示的に特殊化されている場合、その特殊化は、すべての変換で、暗黙的なインスタンス化を引き起こす特殊化の最初の使用の前に宣言されるものとします。そのような使用が行われる単位。診断は必要ありません。プログラムが明示的な特殊化の定義を提供しておらず、特殊化が暗黙的なインスタンス化を発生させるような方法で使用されているか、メンバーが仮想メンバー関数である場合、プログラムは不正な形式であり、診断は必要ありません。宣言されているが定義されていない明示的な特殊化に対して、暗黙的なインスタンス化が生成されることはありません。[...]

これは実際には、プライマリ テンプレート定義にメンバー関数の 1 つの特殊化されていないバージョンの定義が含まれている場合にのみ効果があると思います。その場合、明示的な特殊化が宣言されていない場合、既存のプライマリ定義を使用して関数をインラインでコードにコンパイルする可能性があり、特殊化はリンク時に使用されないことになります。

つまり、プライマリ テンプレート定義にメンバー関数の定義が含まれていない場合、リンカー トリックはおそらく実際には機能すると期待できますが、標準の内容に準拠していないため、ユーザーを夢中にさせる可能性があります。プライマリ テンプレートにインライン関数定義を追加するとすぐに、実際の問題が発生します。

于 2013-02-25T07:21:29.857 に答える