C++ 11のstatic constexpr
メンバー変数に関していくつか混乱があります。
template<typename T>
struct cond_I
{ static constexpr T value = 0; };
// specialization
template<typename T>
struct cond_I< std::complex<T> >
{ static constexpr std::complex<T> value = {0,1}; };
cout << cond_I<double>::value << endl; // this works fine
cout << cond_I< complex<double> >::value << endl; // linker error
ただし、次の行をfirst.hpp
に追加すると、すべて正常に動作します。
template<typename T1>
constexpr std::complex<T1> cond_I< std::complex<T1> >::value;
私が理解していること(私は間違っている可能性があります)は、cond_I< std::complex<double> >::value
には定義が必要ですが、前のケースでは宣言しかないということです。しかし、cond_I<double>::value
はどうですか?なぜそれは定義を必要としないのですか?
もう一度、別のヘッダーファイルsecond.hpp
には、
// empty struct
template<typename T>
struct eps
{ };
// special cases
template<>
struct eps<double>
{
static constexpr double value = 1.0e-12;
};
template<>
struct eps<float>
{
static constexpr float value = 1.0e-6;
};
この場合、以下のコードはeps<>::value
の定義がなくても完全に機能します。
cout << eps<double>::value << endl; // works fine
cout << eps<float>::value << endl; // works fine
これらのシナリオでのstatic constexpr
メンバー変数のさまざまな動作を誰かに説明してもらえますか?
これらの動作は、gcc-5.2
およびclang-3.6
でも同じです。
標準に従って9.4.2/p3静的データメンバー[class.static.data](Emphasis Mine):
不揮発性のconst静的データメンバーが整数型または列挙型である場合、クラス定義での宣言は、割り当て式であるすべての初期化子句が定数式であるブレースまたはイコール初期化子を指定できます(5.20 )。 リテラル型の静的データメンバーは、
constexpr
指定子を使用してクラス定義で宣言できます。宣言されている場合、その宣言は、すべての初期化子節がは代入式です。定数式です。[注:メンバーは定数式で表示される可能性があります。—終了注記]メンバーが名前空間スコープで定義されている場合、メンバーがodrで使用されている場合(3.2)プログラムと名前空間スコープの定義には初期化子を含めないでください。
M.Mが前にコメントで説明したように、ostream::operator<<(ostream&, const complex<T>&)
は参照渡しなので、値はプログラムでodrで使用されていると見なされます。したがって、上記の表現が示すように、定義を提供する必要があります。
基本的な型が値によって渡されることがすでにわかっているので、そのため、定義は必要ありません。