20

次の C++11 プログラム:

int x = 42;

void f()
{
        int y = 43;

        static_assert(&x < &y, "foo");
}

int main()
{
        f();
}

次のように、gcc 4.7 ではコンパイルされません。

error: ‘&amp;y’ is not a constant expression

これは私の直感と一致します。のアドレスは のy呼び出しごとに変わる可能性がfあるため、もちろん変換中に計算することはできません。

ただし、5.19 [expr.const] の箇条書きのいずれも、定数式であることを排除していないようです。

私が見る唯一の2つの候補は次のとおりです。

左辺値から右辺値への変換...

しかし、私が間違っていない限り (?)、プログラムには左辺値から右辺値への変換はありません。

次のid-expression場合を除き、変数を参照する [中略]:

  • 定数式で初期化されます

つまりy、定数式で初期化されます43

これは標準のエラーですか、それとも何か不足していますか?

アップデート:

それは地獄のように紛らわしいですが、私はそれの上にあると思うので、何が起こっているかを示す例を示しましょう:

int x = 42;

void f()
{
        int y = 43;

        // address constant expressions:    
        constexpr int* px = &x; // OK
        constexpr int* py = &y; // ERROR: pointer context for local variable

        // boolean constant expressions:
        constexpr bool bx = &x; // OK
        constexpr bool by = &y; // OK

        // comparison constant expressions:
        constexpr bool eq = (&x == &y); // OK
        constexpr bool lt = (&x < &y); // ERROR: undefined behaviour disqualifies 
                                                 a constant expression
}

int main()
{
        f();
}

最初に、コア定数式 (5.19p2) と定数式 (5.19p4) を区別します。具体的には、定数式のサブ式は、定数式ではなく、コア定数式である必要があります。つまり、定数式であることは、サブ式ではなく、完全な式のプロパティです。さらに、完全な式が使用されているコンテキストを調べる必要があります。

そのため、gcc エラーは誤解を招くものであることが判明しました。First&yは、一部のコンテキストでは定数式である場合があります。第二に、定数式ではない理由&x < &yは、サブ式ではなく、無関係なポインターの比較によるもの&yです。

4

6 に答える 6

12

n3485 を使用して、 static_assert 宣言の式がどの要件を満たす必要があるかを段階的に判断してみましょう。

[dcl.dcl]/1

static_assert-declaration:
static_assert (定数式,文字列リテラル) ;

[dcl.dcl]/4

static_assert-declarationでは、constant-expressionは、文脈上 に変換できる定数式でなければなりませんbool

[expr.const]/4

リテラル定数式参照定数式アドレス定数式をまとめて定数式と呼びます。


では、定数式の種類は何&x < &yですか? アドレス定数式ではありませ:

[expr.const]/4

アドレス定数式std::nullptr_tは、型またはポインター型 [...]のprvalue コア定数式 (コンテキストで必要な変換後) です。

の型は&x < &y[ boolexpr.rel]/1 の通りです。

これも参照定数式ではないため、リテラル定数式でなければなりませ

リテラル定数式は、リテラル型のprvalue コア定数式です [...]

したがって、コア定数式&x < &yの要件を満たす必要があります。


コメントで TemplateRexとhvdが指摘しているように、この特定のケースでは、コア定数式&x < &yの要件を満たしていません。

[expr.const]/2

[コア定数式に含めることはできません] 結果が指定されていない関係演算子または等価演算子。

[expr.rel]/2

同じ型の2 つのポインターpqが、同じオブジェクトのメンバーではない異なるオブジェクト、同じ配列の要素、または異なる関数を指している場合、またはそのうちの 1 つだけが null である場合、 、 、 、および の結果p<qp>qp<=q規定p>=qです。

ただし、次のような例では

int arr[2] = {1, 2};
static_assert(&a[0] < &a[1], "");

この式a < a+1は、この要件も満たしています。

于 2013-08-21T09:30:47.523 に答える
5

ここで何かのアドレスを取得することは問題ではなく、operator<関連のないオブジェクトを使用したポインター比較です。

ポインターの関係演算子は、同じクラス内または配列内のオブジェクトへのポインターに対してのみ指定されます (5.9 関係演算子 [expr.rel]、ポイント 3 および 4。)関係のないオブジェクトへのポインターの関係比較は指定されていません。

順序付けではなく等しいかどうかを比較するアドレスは機能します。

int x = 42;

void f()
{
        int y = 43;

        static_assert(&x != &y, "foo");
                         ^^ <--- "<" on unrelated objects is unspecified
}

int main()
{
        f();
}

実際の例

これが const 式自体とは何の関係もないことを示すために、

void f()
{
        int y[2] = { 42, 43 };

        static_assert(&y[0] < &y[1], "foo");
                            ^ <--- "<" on objects within an array is specified
}

int main()
{
        f();
}

別の実例

于 2013-08-21T08:39:54.237 に答える
1

申し訳ありませんが、前の回答はおそらくアイテムの読み方が間違っていたという点に同意します。代わりに、実際に関連する節は 5.19 [expr.const] 段落 3 であり、次のように書かれています (強調表示が追加されています)。

リテラル定数式は、リテラル型の prvalue コア定数式ですが、ポインター型ではありません. 整数定数式は、整数型またはスコープなし列挙型のリテラル定数式です。[注: このような式は、配列境界 (8.3.4、5.3.4)、ビットフィールド長 (9.6)、基になる型が固定されていない場合の列挙子初期化子 (7.2)、null ポインター定数 (4.10) として使用できます。 )、およびアライメントとして (7.6.2)。—終わりの注 ] 型 T の変換された定数式は、リテラル定数式であり、暗黙的に型 T に変換されます。ここで、暗黙的な変換 (存在する場合) はリテラル定数式で許可され、暗黙的な変換シーケンスにはユーザー定義の変換のみが含まれます。左辺値から右辺値への変換 (4.1)、整数昇格 (4.5)、および縮小変換 (8.5.4) 以外の整数変換 (4.7)。[ 注: そのような式は case 式 (6.4.2) として使用できます。基になる型が固定されている場合は列挙子の初期化子として (7.2)、整数型または列挙型の非型テンプレート引数として (14.3)。—終了注 ] 参照定数式は、静的ストレージ期間または関数を持つオブジェクトを指定する左辺値コア定数式です。アドレス定数式は、静的ストレージ期間を持つオブジェクトのアドレス、関数のアドレス、NULL ポインター値、または型 std の prvalue コア定数式に評価されるポインター型の prvalue コア定数式です。 :nullptr_t. リテラル定数式、参照定数式、アドレス定数式をまとめて定数式と呼びます。

コア定数式は直接定数式ではありません。この 3 番目の段落で説明されている追加の条件があります。

于 2013-08-21T08:40:05.997 に答える
1

5.19p2 は定数式を定義していません。コア定数式を定義しています。

コア定数式は、5.19p3 の規則の 1 つに準拠している場合にのみ定数式になります。そこで、関連する部分はすでに jrok によって指摘されています。

アドレス定数式は、静的記憶域期間を持つオブジェクトのアドレス、関数のアドレス、null ポインター値、または型の prvalue コア定数式に評価されるポインター型の prvalue コア定数式ですstd::nullptr_t

コア定数式&yはそれらのいずれにも評価されないため、アドレス定数式ではないため、定数式ではありません

于 2013-08-21T08:48:09.637 に答える