8

これは、別の質問に与えられた短い回答と、それに関連する C++11 標準のいくつかの側面について、より完全な説明を求めているという点で、おそらく少し変わった質問です。

参照しやすいように、ここで参照されている質問を要約します。OP はクラスを定義します。

struct Account 
{
    static constexpr int period = 30;
    void foo(const int &) { }
    void bar() { foo(period); } //no error?
};

また、クラス内で初期化された静的データ メンバーの使用についてエラーが発生しないのはなぜだろうと考えています (ある本では、これは違法であると述べられています)。Johannes Schaubの答えは、次のように述べています。

  1. これは、 One Definition Ruleに違反しています。
  2. 診断は必要ありません。

この回答のソースと有効性に依存している限り、私は個人的にそれが難解すぎると感じているため、正直に嫌いです. 関連するのは § 9.4.2/4 のようです:

「プログラムには、 odr で使用される(3.2)静的データ メンバーの定義が 1 つだけ存在する必要があります。診断は必要ありません [強調は私のものです]

これにより、ポイントに少し近づきます。そして、これは § 3.2/2 がどのようにodr で使用される変数を定義するかです:

「潜在的に評価される式として現れる名前を持つ変数は、それが定数式 (5.19) に現れるための要件を満たし、左辺値から右辺値への変換 (4.1) がすぐに適用されるオブジェクトでない限り、odr 使用れます [強調は私のものです]

OPの質問では、変数は変数periodである定数式に現れるための要件を明確に満たしていconstexprます。したがって、理由は確実に2 番目の条件にあるはずです: "そして、左辺値から右辺値への変換 (4.1) はすぐに適用されます"

これは、標準の解釈に問題があるところです。この 2 番目の条件は、実際には何を意味するのでしょうか。それがカバーする状況は何ですか?関数から返された場合、静的constexpr変数はodrで使用されない(したがって、クラス内で初期化できる)ということですか?

より一般的には、クラス内で初期化できるように、静的変数で何をすることが許可されていますか?constexpr

4

2 に答える 2

3

あなたは前提の一部を逃しました。Account::period上記のクラス定義は、どこかで定義する場合 (ただし、初期化子を提供しない場合) は完全に有効です。9.4.2/3 の最後の文を参照してください。

メンバーは、プログラムで ODR 使用 (3.2) され、名前空間スコープ定義に初期化子が含まれていない場合でも、名前空間スコープで定義されます。

この全体の説明は、名前空間スコープの静的変数ではなく、静的 constexpr データ メンバーにのみ適用されます。静的データ メンバーがconstexpr(単なる const ではなく) の場合、クラス定義でそれらを初期化する必要があります。したがって、あなたの質問は本当に次のようになるはずです:名前空間スコープで定義を提供する必要がないように、静的な constexpr データメンバーで何をすることが許可されていますか?

上記から、答えはメンバーがodr-usedであってはならないということです。そして、 odr-usedの定義の関連部分をすでに見つけました。したがって、評価されない可能性のあるコンテキストでメンバーを使用できます-未評価のオペランド(sizeofor などdecltype)またはその部分式として。また、左辺値から右辺値への変換がすぐに適用される式で使用できます。

これで、左辺値から右辺値への即時変換を引き起こす変数の使用は何ですか?

その答えの一部は§5/8にあります:

glvalue 式が、そのオペランドの prvalue を予期する演算子のオペランドとして現れるときはいつでも、左辺値から右辺値へ (4.1)、配列からポインターへ (4.2)、または関数からポインターへ (4.3) の標準変換が行われます。式を prvalue に変換するために適用されます。

標準の算術変換を適用するすべての演算子に基本的に適用される算術型の場合。したがって、定義を必要とせずに、さまざまな算術演算および論理演算でメンバーを使用できます。

[g]lvalue または [p]rvalue であるという要件は標準全体に広がっているため、すべてを列挙することはできません。ここでできることとできないことです。経験則は次のとおりです。変数ののみが使用される場合、左辺値から右辺値への変換が適用されます。変数がオブジェクトとして使用される場合は、左辺値として使用されます。

左辺値が明示的に必要なコンテキストでは使用できません。たとえば、アドレス取得演算子または変更演算子への引数として使用できません)。左辺値参照の直接バインディング (変換なし) は、そのようなコンテキストです。

いくつかの例 (詳細な標準分析なし):

関数パラメーターが変数を直接バインドできる参照でない限り、関数に渡すことができます。

関数から返す場合: 戻り値の型への暗黙的な変換にユーザー定義の変換関数が含まれる場合、関数に渡すための規則が適用されます。それ以外の場合は、関数が変数を直接参照する左辺値 (参照) を返さない限り、それを返すことができます。

ODR で使用される変数の重要な規則である「1 つの定義規則」は 3.2/3 にあります。

すべてのプログラムには、そのプログラムで ODR で使用されるすべての非インライン関数または変数の定義が 1 つだけ含まれている必要があります。診断は必要ありません。

「診断不要」の部分は、この規則に違反するプログラムが未定義の動作を引き起こすことを意味します。これは、コンパイルの失敗、コンパイルの失敗から、コンパイルの失敗、すべてが正常であるかのように振る舞うものまでさまざまです。また、コンパイラは問題について警告する必要はありません。

その理由は、他の人がすでに示したように、これらの違反の多くはリンカーによってのみ検出されるためです。ただし、最適化によってオブジェクトへの参照が削除された可能性があるため、リンクの失敗の原因が残っていないか、リンクが実行時にのみ発生するか、名前の複数の定義から任意のインスタンスを選択するように定義されている可能性があります。

于 2013-01-27T15:34:23.237 に答える
3

関数から返された場合、静的な constexpr 変数は ODR で使用されない (したがって、クラス内で初期化できる) ということですか?

はい。

基本的に、それをobjectではなくvalueとして扱う限り、それは ODR で使用されません。値を貼り付けた場合、コードは同じように機能することを考慮してください。これは、右辺値として扱われる場合です。しかし、そうではないシナリオがいくつかあります。

プリミティブで左辺値から右辺値への変換が実行されないシナリオはごくわずかです。それは参照バインディングで&objあり、おそらく他にもいくつかありますが、非常に少数です。コンパイラが へのconst int&参照を提供する場合、そのperiodアドレスを取得できなければならず、さらに、このアドレスは各 TU で同じでなければならないことに注意してください。つまり、C++ の恐ろしい TU システムでは、1 つの明示的な定義が必要です。

ODR で使用されていない場合、コンパイラは各 TU でコピーを作成するか、値を置き換えるか、必要なものを何でも使用できますが、違いを観察することはできません。

于 2013-01-27T14:10:52.747 に答える