32

Copy and Swapを読んでいました。

Copy Elision のいくつかのリンクを読んでみましたが、それが何を意味するのかを正しく理解できませんでした。誰かがこの最適化とは何か、特に次のテキストの意味を説明してもらえますか

これは単なる利便性の問題ではなく、実際には最適化です。パラメーターが左辺値 (別の非 const オブジェクト) にバインドされている場合、パラメーターの作成中にオブジェクトのコピーが自動的に作成されます。ただし、s が右辺値 (一時オブジェクト、リテラル) にバインドされている場合、通常、コピーは省略され、コピー コンストラクターとデストラクターへの呼び出しが節約されます。パラメーターが const 参照として受け入れられる代入演算子の以前のバージョンでは、参照が右辺値にバインドされている場合、コピー省略は発生しません。これにより、追加のオブジェクトが作成および破棄されます。

4

2 に答える 2

35

コピー コンストラクターは、コピーを作成するために存在します。理論的には、次のような行を書くと:

CLASS c(foo());

コンパイラは、コピー コンストラクターを呼び出して、 の戻り値を にコピーする必要がありfoo()ますc

コピー省略は、コピー コンストラクターの呼び出しをスキップして、オーバーヘッドを支払わないようにする手法です。

たとえば、コンパイラは、 がfoo()その戻り値を に直接構築するように手配できますc

別の例を次に示します。関数があるとしましょう:

void doit(CLASS c);

実引数で呼び出す場合、元のパラメーターを変更できないように、コンパイラーはコピー コンストラクターを呼び出す必要があります。

CLASS c1;
doit(c1);

しかし、別の例を考えてみましょう。関数を次のように呼び出すとします。

doit(c1 + c1);

operator+一時オブジェクト (右辺値) を作成する必要があります。を呼び出す前にコピー コンストラクターを呼び出す代わりにdoit()、コンパイラーは によって作成された一時を渡し、代わりにoperator+それを に渡すことができdoit()ます。

于 2010-01-27T00:52:30.510 に答える
2

次に例を示します。

#include <vector>
#include <climits>

class BigCounter {
 public:
   BigCounter &operator =(BigCounter b) {
      swap(b);
      return *this;
   }

   BigCounter next() const;

   void swap(BigCounter &b) {
      vals_.swap(b);
   }

 private:
   typedef ::std::vector<unsigned int> valvec_t;
   valvec_t vals_;
};

BigCounter BigCounter::next() const
{
   BigCounter newcounter(*this);
   unsigned int carry = 1;
   for (valvec_t::iterator i = newcounter.vals_.begin();
        carry > 0 && i != newcounter.vals_.end();
        ++i)
   {
      if (*i <= (UINT_MAX - carry)) {
         *i += carry;
      } else {
         *i += carry;
         carry = 1;
      }
   }
   if (carry > 0) {
      newcounter.vals_.push_back(carry);
   }
   return newcounter;
}

void someFunction()
{
    BigCounter loopcount;
    while (true) {
       loopcount = loopcount.next();
    }
}

somefunction行内では、コピーの省略loopcount = loopcount.next();から大きなメリットが得られます。コピーの省略が許可されていない場合、その行では、コピー コンストラクターの 3 回の呼び出しと、関連するデストラクターへの呼び出しが必要になります。BigCount::next()コピーの省略が許可されているため、コピー コンストラクターの 1 回の呼び出しに減らすことができますnewcounter

operator =次のように宣言および定義されていた場合:

BigCounter &BigCounter::operator =(const BigCounter &b) {
   BigCounter tmp(b);
   swap(tmp);
   return *this;
}

コピーの省略があっても、コピー コンストラクターを 2 回呼び出す必要がありました。1 つは構築newcounterし、もう1 つは構築しtmpます。そして、コピーの省略がなければ、まだ 3 になります。そのoperator =ため、代入演算子に「コピー アンド スワップ」イディオムを使用する場合、その引数がコピー コンストラクトの呼び出しを必要とするように宣言することが最適化になる可能性があります。引数を作成するためにコピー コンストラクターが呼び出されると、その呼び出しが省略される場合がありますが、ローカル変数を作成するために呼び出される場合は省略されない場合があります。

于 2010-01-28T08:12:13.763 に答える