C ++ 17はインライン変数を導入します
C ++ 17 constexpr static
は、odrで使用された場合に行外定義を必要とするメンバー変数のこの問題を修正します。C ++ 17より前の詳細については、この回答の後半を参照してください。
提案P0386インライン変数は、inline
指定子を変数に適用する機能を導入します。特にこの場合は、静的メンバー変数をconstexpr
意味inline
します。提案は言う:
インライン指定子は、関数だけでなく変数にも適用できます。インラインで宣言された変数は、インラインで宣言された関数と同じセマンティクスを持っています。複数の翻訳単位で同じように定義でき、odrを使用するすべての翻訳単位で定義する必要があり、プログラムの動作は次のようになります。変数は1つだけです。
変更された[basic.def] p2:
...でない限り、宣言は定義です。
- クラス定義の外部で静的データメンバーを宣言し、変数はconstexpr指定子を使用してクラス内で定義されました(この使用法は非推奨です。[depr.static_constexpr]を参照してください)。
...
そして[depr.static_constexpr]を追加します:
以前のC ++国際標準との互換性のために、consistpr静的データメンバーは、イニシャライザを使用せずに、クラスの外部で冗長に再宣言される場合があります。この使用法は非推奨です。[例:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
—例を終了]
C ++ 14およびそれ以前
C ++ 03では、const積分またはconst列挙型のクラス内初期化子のみを提供できました。C++ 11では、constexpr
これを使用してリテラル型に拡張されました。
C ++ 11では、我々は静的のための名前空間スコープを定義する必要はありません。constexpr
そうでない場合はメンバーのODR-使用し、我々はドラフトC ++ 11標準のセクションからこれを見ることができます9.4.2
[class.static.data]と言います(今後の重点鉱山):
[...]リテラル型の静的データメンバーは、constexpr指定子を使用してクラス定義で宣言できます。もしそうなら、その宣言は中括弧イコールイニシャライザを指定しなければなりません。ここで、代入式であるすべての初期化句は定数式です。[注:これらのどちらの場合でも、メンバーは定数式に現れることがあります。—end note]
プログラムでメンバーがodr-used(3.2)であり、ネームスペーススコープ定義に初期化子が含まれていない場合、メンバーはネームスペーススコープで定義されます。
それで問題は、ここでbaz
odr-usedです:
std::string str(baz);
答えはyesなので、名前空間スコープの定義も必要です。
それでは、変数がodrで使用されているかどうかをどのように判断しますか?セクション3.2
[basic.def.odr]の元のC ++ 11表現は次のように述べています。
式は、未評価のオペランド(第5項)またはその部分式でない限り、潜在的に評価されます。名前が潜在的に評価された式として表示される変数は、定数式(5.19)に表示されるための要件を満たし、左辺値から右辺値への変換(4.1)がすぐに適用される
オブジェクトでない限り、odr使用されます。
したがってbaz
、定数式が生成されますが、左辺値から右辺値への変換はbaz
、配列であるため適用できないため、すぐには適用されません。これについては、次のセクション4.1
[conv.lval]で説明されています。
非関数、非配列タイプTのglvalue(3.10)は、prvalue.53 [...]に変換できます。
配列からポインターへの変換で適用されるもの。
[basic.def.odr]のこの表現は、一部のケースがこの表現でカバーされなかったため、不具合レポート712により変更されましたが、これらの変更はこのケースの結果を変更しません。