5

注:明確にするために、質問はrestrict一般的なキーワードの使用に関するものではなく、具体的にはここで説明されているメンバー関数への適用に関するものです。

gcc を使用すると、__restrict__(C99 の に相当する GNU++ のrestrict) 修飾子をメンバー関数でthis使用でき、関数のスコープ内で限定修飾ポインターを効果的に作成できます。牛肉はどこ?

ほとんどのメンバー関数は他のメンバーで動作しthis、 を介してアクセスしますT* const(通常はエイリアスされていません)。エイリアスされる可能性があるためthis、メンバー関数内で何らかの形で使用されている型への 2 番目のポインターが必要であり、それはどこかから取得する必要があります。
これは、たとえば、すべての二項演算子や、少なくとも 2 つのポインターまたは同一の非自明な型の参照を取るその他の自由関数など、非メンバー関数の場合によくある状況です。ただし、これらの関数には がないthisため、関係ありません。

代入演算子、コピー コンストラクター、および単項比較演算子は 、原則としてエイリアス化this できるメンバー関数の例です (別のオブジェクトが参照を介して渡されるため)。したがって、これらに restrict 修飾子を割り当てることは本当に意味があります。コンパイラーにとっては、他のすべての関数が restrict プロパティを持っていることはすでに明らかなはずです (T への 2 番目のポインターが存在しないため)。

たとえば、restrictonを使用したoperator=場合、結果として自己割り当てをまったくチェックしないでください。これthisは、その関数のスコープ内でエイリアス化されていないと言っているためです ( true の場合、自己割り当ては発生しない可能性があります)。
明らかに、これは事前に知ることができないものであり、意味をなさないものでもあります.

では、実際にメンバー関数に制限修飾子を与えたい場合と、それが理にかなっているのはどのような場合でしょうか?

4

5 に答える 5

4

何かが足りないか、あなたの質問が意味をなさないかのどちらかです。 メンバー関数への他の引数とthisそれほど変わらないのにrestrict、なぜ GCC がそれに適用できることに驚くのでしょうか?

それを代入演算子に適用することに関して、あなたはそれが明示的な自己代入テストの必要性を取り除くだろうと正しく指摘します。次に、次のように言います。

明らかに、これは事前に知ることができないものです

しかし、これは何かに使用する場合に常に当てはまります。restrictたとえば、memcpyオーバーラップするメモリ領域で呼び出しを行う人がいるかもしれません。彼らがそうしないことを「おそらく事前に知ることはできません」。しかし、restrictの引数の宣言は、もしそうならエラーを犯したことmemcpyを意味します。まったく同じように、代入 operator を宣言すると、誰かがそのクラスのオブジェクトを自己代入することはエラーになります。これには、不思議なことや矛盾することはまったくありません。これは、コードの残りの部分に特定の制約を課すというセマンティクスの一部にすぎません。restrictrestrict

また、メンバー関数が同じ型の別のオブジェクトへのポインター (または参照) を取ることが不可能であると感じる理由もわかりません。些細な例:

class Point {
public:
    double distance(const Point &other) const;
};

この種のことは常に発生します。

本当の質問は、なぜthis他の議論とそんなに違うと思いますか? または、もしよろしければ、どうして私はあなたの要点を完全に見逃してしまったのですか?

于 2011-07-25T05:23:24.347 に答える
2

皆さんが見逃しているのは、メンバー関数への引数がパーツまたはオブジェクトのエイリアスになる可能性があることだと思います。これが例です

struct some_class {
    int some_value;

    void compute_something(int& result) {
        result = 2*some_value;
        ...
        result -= some_value;
    }
}

おそらく、それがコンパイルされると予想されるでしょう

*(this + offsetof(some_value)) -> register1
2*register1 -> register2
...
register2 - register1 -> result

残念ながら、誰かが結果の some_value への参照を渡した場合、そのコードは間違っています。したがって、コンパイラは実際に次のように生成する必要があります

*(this + offsetof(some_value)) -> register1
2*register1 -> register2
register2 -> result

...
*(this + offsetof(some_value)) -> register1
result -> register2
register2 - register1 -> register2
register2 -> result

これは明らかに効率が悪いです。compute_something がインラインでない限り、コンパイラはresult が some_value にエイリアスを設定するかどうかを知る方法がないため、賢明か愚かかに関係なく、最悪のケースを想定する必要があることに注意してください。したがって、たとえ this ポインターに適用されたとしても、制限する明確で非常に現実的な利点があります。

于 2012-04-21T23:57:01.357 に答える
1

あなたが投稿したリンクは興味深いものです。restrictに適用したための確実なユースケースはありませんthis。質問で述べたように、コピーコンストラクター、演算子=は潜在的な候補であった可能性があります。しかし、コンパイラはそれらを処理できます。

しかし、次のケースは興味深いものになる可能性があります

struct A
{
  //...
  void Destroy (A*& p) __restrict__
  {
    delete this;
    p = 0;
    p++;
  }
};

これで、ユースケースは次のようになります。

A **pp = new A*[10];
for(int i = 0; i < 10; i++)
  pp[i] = new A;
//...
A* p = pp[0];
for(int i = 0; i < 10; i++)
  p->Destroy(p);
delete[] pp;

これは非常に珍しい方法ですが、このユースケースについて考えることができるのはこれだけです。

于 2011-07-25T03:58:19.163 に答える
0

残念ながら、なぜあなたが について話しているのか、私にははっきりと理解できませんthis

restrictポインターが他のポインターと重複しないことを指定します。そのため、コンパイラは制限ポインタが指すメモリ領域が依存していないと想定できるため、より積極的な最適化が可能になります。__restrict__以外のポインター変数に使用すると、はるかに効果的ですthis

では、実際にメンバーに制限修飾子を与えたい場合と、それが理にかなっているのはどのような場合でしょうか?

restrictでポインタを使用する代表的なケースを思い出してmemcpyください。

void Foo::MyCompute(__restrict__ char* bufA, __restrict__ char* BufB)
{
}
于 2011-07-24T17:56:46.093 に答える
0

これを回答として追加するのは、おそらくそのほうが適しているからです(これは一種の回答であり、実際には質問に属していません。また、コメントには少し長いです)。

Nemo の答えを長い間考えた結果、自己割り当てに関する私たちの両方の解釈はおそらく多少間違っていると思います (ただし、Nemo のほうが私のものよりも正しかったです)。Nemo が正しく指摘したように、restrict-qualified を持つことは、this実際にはエイリアシングの存在がプログラム エラーであることを意味します。それ以上でもそれ以下でもありません。

限り、これを書くとき、あなたのロジックは実際には「これは起こり得ないと言っているので、必然的に自己割り当てをチェックするべきではありません」ではなく、むしろ「エイリアシングは起こり得ないと明示的に言い、それがプログラムエラーである場合はプログラムエラーです」そうであれば、自己割り当てをチェックする必要があるだけでなく、それが起こった場合、必然的に激しく失敗する必要があります。」

そして、これは特定のプログラム ロジックを強調すると同時に、コンパイラがその特定の特殊なケースに対してより適切に最適化できるようにするため、実際に理にかなっています。

于 2011-07-25T12:11:39.147 に答える