このコードを考えてみましょう:
#include <vector>
struct A {
static constexpr int kDefaultValue = -1;
std::vector<int> v;
A(int n): v(n, A::kDefaultValue) {}
};
int main() {
A(10);
return 0;
}
リンクに失敗する(llvm clang、gcc 4.9、OS Xでは両方):
Undefined symbols for architecture x86_64:
"A::kDefaultValue", referenced from:
A::(int) in main.cpp.o
ld: symbol(s) not found for architecture x86_64
問題はそれの何が問題なのですか? static_cast
- ing A::kDefaultValue
to int
で修正できます。または、kDefaultValue
をA
から移動します。どちらのケースも醜いようです。これはリンクする別の方法ですか?
この振る舞いは私を何度も悩ませています。トラブルの原因は、
_A(int n): v(n, A::kDefaultValue) {}
_
odr-uses _static constexpr
_メンバー、v
のコンストラクターは定数参照を取得するため第二引数。 Odrの使用には、どこかに定義が必要です。
_const int A::kDefaultValue;
_
一部のコンパイルユニット(コンパイルされ、main()
にリンクされます)。この要件はC++ 17で削除され、対応する定義(上記)は廃止されました。
ただし、定義が常に可能であるとは限らず(たとえば、クラステンプレートのメンバーの場合)、定義とエラーの両方を回避する最も簡単な方法は次のとおりです。
_A(int n): v(n, int(A::kDefaultValue)) {}
_
これは、v
のコンストラクターに渡される一時ファイルを作成します(ただし、後者は完全にインラインであるため、コンパイラーはそれを最適化する場合があります)。
C++ 17以降、動作が変更されました。 C++ 17より前は、 constexpr静的データメンバー でもクラス定義内で初期化する必要がありますが、名前空間スコープでの定義は引き続き必要です。 C++ 17以降、名前空間スコープ定義は再び必要ありません。
静的データメンバーがconstexprとして宣言されている場合、それは暗黙的にインラインであり、名前空間スコープで再宣言する必要はありません。イニシャライザ(以前は上記のように必須でした)を使用しないこの再宣言は引き続き許可されますが、非推奨です。 (C++ 17以降)
C++ 17をサポートするコンパイラを使用してコードをコンパイルすると、問題なく動作します。