9

この質問に対する私の回答に関する議論に続いて、どうやら:

次のコード許可されています

struct Foo {
    int x;
};

Foo f;
Foo & f_ref = f;

(&f) -> ~Foo ();
new (&f) Foo ();

int x = f_ref .x;

ただし、次のコードは許可されていません

struct Foo {
    const int & x;           // difference is const reference
    Foo (int & i) : x(i) {}
};

int i;
Foo f (i);
Foo & f_ref = f;

(&f) -> ~Foo ();
new (&f) Foo (i);

int x = f_ref .x;

$3.8/7のため

オブジェクトの有効期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、元のオブジェクトが占有していたストレージの場所に新しいオブジェクトが作成された場合、元のオブジェクトを指すポインタ、その参照または、元のオブジェクトの名前が自動的に新しいオブジェクトを参照し、新しいオブジェクトの有効期間が開始されると、新しいオブジェクトを操作するために使用できます。

  • 元のオブジェクトの型が const 修飾されておらず、クラス型の場合、型が const 修飾されているか参照型である非静的データ メンバーが含まれていない...

f.xf が存在しなくなったときにへの参照を無効にする方法は理解できますf_refが、そのメンバーの 1 つが const および/または参照であり、それ以外ではないという理由だけで無効にする必要があるFoo理由がわかりません。Fooその後への参照です。

誰かがこの状態の背後にある理論的根拠を説明できますか?

編集

答えてくれてありがとう。現在、オプティマイザーがリファランドをキャッシュすることを許可していないため、「変更されないことを保証する」という引数は購入しません。次に例を示します

struct Foo {
    const int & x;
    Foo (const int & i) : x(i) {}
    void do_it ();
};

int i;
Foo f (i);
const int & ii = f.x;

f .do_it (); // may modify i
std :: cout << ii; // May NOT use cached i

do_it参照された値を無効にする方法がわかりませんが、そうoperator newではありません -- シーケンス ポイントはキャッシュされた値を無効にします。delete/placement-new を除外する必要があるのはなぜですか?

4

4 に答える 4

8

動機は、コンパイラーがオブジェクトの値をキャッシュできるようにすることだと思います (単に const へのポインターと const への参照の参照元ではなく、constconstオブジェクトであることに注意してください)。コード。

2番目の例では、コンパイラは、最初にオブジェクトが作成および破棄されたことを「見る」ことができ、次に同じ値を使用して再作成されたことを確認できます。しかし、標準の作成者は、コンパイラがこのコードを変更できるようにすることを望んでいました。

struct Foo {
    const int & x;
    Foo (int & i) : x(i) {}
};

int i = 1;
Foo f(i);

some_function_in_another_TU(&f);

std::cout << f.x;

これに:

struct Foo {
    const int & x;
    Foo (int & i) : x(i) {}
};

int i = 1;
Foo f(i);

some_function_in_another_TU(&f);

std::cout << i;           // this line is optimized

の参照メンバはf再装着できないため、引き続き を参照する必要iがあります。破壊と構築の操作は、参照メンバーの非再挿入可能性に違反していますx

この最適化は特に物議を醸すものではありません。または参照メンバーconstを持つオブジェクトではなく、オブジェクトを使用する次の例を検討してください。const

const int i = 1;
some_function_in_another_TU(&i);
std::cout << i;

これiはコンパイル時の定数であり、some_function_in_another_TU有効に破棄してint別の値でその場所に別の定数を作成することはできません。そのため、コンパイラはコードを発行できるようにする必要がありますstd::cout << 1;。アイデアは、他の型の const オブジェクトと参照の類推によって同じことが当てはまるはずであるということです。

不明なコードの呼び出しが参照メンバーを再配置したり、constデータ メンバーの値を変更したりする可能性がある場合、言語の有用な不変条件 (参照は再配置されず、const オブジェクトはその値を決して変更しない) が壊れます。

于 2011-09-28T17:09:42.307 に答える
5

私が知る限り、これはセマンティックの正確さと、オプティマイザーが行う可能性のある忠実な仮定の問題です。このことを考慮:

Bar important, relevant;

Foo x(important);  // binds as const-reference

Zoo z(x);  // also binds as const reference

do_stuff(z);

x.~Foo();
::new (&x) Foo(relevant);  // Ouch?

オブジェクトは、そのメンバー参照が一定であるzことを合理的に期待し、したがって を参照します。標準が言うように、最後の 2 行の破棄と新しい構築は、「(論理的に) 新しいオブジェクトを参照するようにすべての参照を自動的に更新する」ため、定数であるという約束にもかかわらず、内部の const-reference が変更されました。Fooimportantz

この const-correctness のバックスタブ違反を回避するために、再構築全体が禁止されています。

于 2011-09-28T17:09:20.137 に答える
2

最適化。私が持っているとしましょう:

struct Foo
{
    int const x;
    Foo( int init ) : x( init ) {}
};

int
main()
{
    Foo a( 42 );
    std::cout << a.x << std::endl;
    new (&a) Foo( 3 );
    std::cout << a.x << std::endl;
    return 0;
}

オブジェクトを見たコンパイラーはconst int、値が変更されないと想定する権利を持っています。オプティマイザーは、新しい配置全体のレジスターに値を保持し、それを再度出力する場合があります。

あなたの例は実際にはかなり異なっていることに注意してください。データメンバーのタイプはint const&;です。これは参照であるため(参照は常にconstです)、コンパイラーは、参照が常に同じオブジェクトを参照していると見なすことができます。i (オブジェクト自体もconstでない限り、このオブジェクトの値は変更される可能性があります。)ただし、コンパイラーは、 (あなたの場合)明らかに変更される可能性があるため、参照するオブジェクトの値についてそのような仮定を行うことはできません。参照自体(すべての参照と同様)が不変であるため、ここで未定義の動作が発生します。これは、const 作成した動作ではありません。

于 2011-09-28T17:27:33.367 に答える
-2

何かが const 修飾されている場合、それを変更することは想定されていません。その寿命を延ばすことは、重大な結果を伴う変更です。(たとえば、デストラクタに副作用があるかどうかを検討してください。)

于 2011-09-28T17:10:30.307 に答える