これらの定数式をどのように使用するかによって異なります。
ODR (1 定義規則) は、次のように述べています。
(§3.2/2) [...] 潜在的に評価される式として現れる名前を持つ変数は、それが定数式 (5.19) および左辺値に現れるための要件を満たすオブジェクトでない限り、odr-used です。右辺値変換 (4.1) がすぐに適用されます。[...]
(そして、多くの特別なルール、例外、および例外の例外が続きます。)
ODR で使用される変数には、正確に 1 つの定義が必要です。あなたの定数式には宣言がありますが、定義はありません。
たとえば、次のようにするとうまくいきます。
int main() {
E e = GetE<float>::type;
return 0;
}
しかし、これはしません:
void f(const E &)
{ }
int main() {
f(GetE<float>::type);
return 0;
}
(const) 参照が必要なためf
、左辺値から右辺値への変換をすぐに適用することはできません。したがって、これは ODR 使用を構成します。コンパイラは、定義が欠落していると文句を言います。
(注。ShafikYaghmour が見つけたように (コメントを参照)、コンパイラが最適化を使用している場合、参照が最適化されていない可能性があるため、苦情が発生しない場合があります。コンパイラの苦情を再現するには、-O0
フラグを使用します (または、コンパイラによっては同様のフラグを使用します) 。 .)
この問題を解決するために、必要な定義を通常の方法で、つまり構造体定義の外で提供することができます。
constexpr E GetE<float>::type;
constexpr E GetE<char>::type;
constexpr E GetE<int>::type;
しかし、これは (ヘッダー ファイルではなく) .cpp で行わなければならないため、2 つの異なる場所で宣言と定義を維持する必要があり、面倒です。
コメントで提案したばかりの解決策、つまり、constexpr (およびインライン) 関数を定義すると、正しく聞こえます。
template <class T> constexpr E GetE();
template <> constexpr E GetE<float>()
{ return TYPE_FLOAT; }
template <> constexpr E GetE<char>()
{ return TYPE_CHAR; }
template <> constexpr E GetE<int>()
{ return TYPE_INT; }
void f(const E &)
{ }
int main() {
E e = GetE<float>();
f(GetE<float>());
return 0;
}