インスタンス化されない限り、プライマリ参照テンプレートを初期化せずに C++14 で参照テンプレートを宣言することは合法ですか?
template<class T>
const T& ref;
template<>
auto ref<int> = 1;
auto x = ref<int>;
これにより、GCC と Clang で異なる結果が生成されます。
$ g++ -std=c++14 -c ref.cpp
$
$ clang -std=c++14 -c ref.cpp
ref.cpp:2:10: error: declaration of reference variable 'ref' requires an
initializer
const T& ref;
^~~
1 error generated.
インスタンス化されるまでは、参照ではなくテンプレートであるため、プライマリ参照テンプレートを初期化する必要はありません。
次のようなことができることがわかりました。
template<class T>
const T& ref = "Meaningless initialization with any value of any type";
template<>
auto ref<int> = 1;
auto x = ref<int>;
どうやら GCC と Clang の両方が、有効な式であり、プライマリ参照テンプレートがインスタンス化されない限り、参照テンプレート初期化子 RHS を受け入れますが無視するためです。また、任意の型の任意の式が Clang の初期化要件を満たします。
プライマリ参照テンプレートがインスタンス化されない限り、GCC はイニシャライザを必要としません。参照テンプレートが実際にインスタンス化されるまでは、初期化子を必要としないため、これは「精神的に」正しい動作のようです。
基準は、参照テンプレートで 100% 明確ではありません。変数テンプレートのインスタンス化で見つけたものは次のとおりです。
14.7.1
変数テンプレートの特殊化が明示的にインスタンス化または明示的に特殊化されていない限り、変数テンプレートの特殊化は、特殊化が使用されるときに暗黙的にインスタンス化されます。
...
実装は、インスタンス化を必要としない ... 変数テンプレート ... を暗黙的にインスタンス化してはなりません。
14.7.2
インライン関数、初期化子または戻り値から推定される型を持つ宣言 (7.1.6.4)、
constリテラル型の変数、参照型の変数、およびクラス テンプレートの特殊化を除き、明示的なインスタンス化宣言には、エンティティの暗黙的なインスタンス化を抑制する効果があります。彼らが参照するもの。[注: その意図は、明示的なインスタンス化宣言の対象であるインライン関数は、odr が使用された場合 (3.2) にも暗黙的にインスタンス化されるため、本体をインライン化すると見なすことができますが、アウトオブラインのコピーはありません。インライン関数の翻訳単位で生成されます。14.7.3
明示的に特殊化されている関数テンプレート、クラス テンプレート、または変数テンプレートの宣言は、明示的な特殊化の宣言に先行するものとします。[注: テンプレートの定義ではなく、宣言が必要です。—終了注]。
編集して追加:
変数テンプレート宣言、クラス テンプレート宣言、または関数テンプレート宣言は、それぞれ変数宣言、クラス宣言、または関数宣言と同じではなく、同じ規則の対象ではありません。テンプレートがインスタンス化されるまでは、それは単なるテンプレートです。
クラス テンプレート、変数テンプレート、および関数テンプレートは、主な定義を提供せずに宣言できますが、特殊化の定義のみを提供できます。次のコードは、Clang と GCC の両方で有効です。
// Class
template<class T> class foo; // Declaration, not definition
template<> class foo<int> {}; // Specialization definition
using ifoo = foo<int>; // Specialization instantiation
// Function
template<class T> void bar(T); // Declaration, not definition
template<> void bar(int) {} // Specialization definition
void (*barp)(int) = bar<int>; // Specialization instantiation
// Variable
int j;
template<class T> T* point; // Declaration, not definition
template<> int* point<int> = &j; // Specialization definition
int *k = point<int>; // Specialization instantiation
では、問題は、なぜ参照テンプレートと異なる必要があるのかということです。他のテンプレートには当てはまらないのに、参照テンプレートの最初の宣言が参照の初期化を伴う定義でなければならないのはなぜですか?
template<class T> const T& ref; // Declaration, not definition
template<> const int& ref<int> = 1; // Specialization definition
const int& iref = ref<int>; // Specialization instantiation