ما هي النسخ Elision وكيف تعمل على تحسين المصطلح والنسخ؟

StackOverflow https://stackoverflow.com/questions/2143787

سؤال

كنت أقرأ نسخ ومبادلة.

حاولت قراءة بعض الروابط على نسخ Elision لكنني لم أستطع معرفة ما يعنيه بشكل صحيح. هل يمكن لأي شخص أن يوضح ماهية هذا التحسين ، وخاصة ما يعنيه النص التالي

هذه ليست مجرد مسألة راحة ولكن في الواقع تحسين. إذا كانت المعلمة (المعلمة) ترتبط بـ LVALUE (كائن آخر غير مؤشر) ، يتم إجراء نسخة من الكائن تلقائيًا أثناء إنشاء المعلمة (المعلمة). ومع ذلك ، عندما ترتبط S بمقصورة (كائن مؤقت ، حرفي) ، تكون النسخة عادة ما تكون محدودة ، والتي تحفظ مكالمة إلى مُنشئ للنسخ ومدمار. في الإصدار السابق من مشغل الواجب حيث يتم قبول المعلمة كمرجع const ، لا تحدث نسخ Elision عندما يرتبط المرجع بـ RValue. هذا ينتج عنه كائن إضافي يتم إنشاؤه وتدميره.

هل كانت مفيدة؟

المحلول

يوجد منشئ النسخ لإجراء نسخ. من الناحية النظرية عندما تكتب خطًا مثل:

CLASS c(foo());

سيتعين على المترجم الاتصال بمنشئ النسخ لنسخ عودة foo() داخل c.

Copy Elish هي تقنية لتخطي استدعاء مُنشئ النسخ حتى لا تدفع مقابل النفقات العامة.

على سبيل المثال ، يمكن للمترجم ترتيب ذلك foo() سوف تبني قيمة الإرجاع مباشرة إلى c.

هذا مثال آخر. لنفترض أن لديك وظيفة:

void doit(CLASS c);

إذا قمت بتسميتها مع وسيطة فعلية ، فيجب على المترجم أن يستدعي مُنشئ النسخ بحيث لا يمكن تعديل المعلمة الأصلية:

CLASS c1;
doit(c1);

ولكن الآن فكر في مثال مختلف ، دعنا نقول أنك تسمي وظيفتك مثل هذه:

doit(c1 + c1);

operator+ سوف تضطر إلى إنشاء كائن مؤقت (rvalue). بدلاً من استدعاء مُنشئ النسخ قبل الاتصال doit(), ، يمكن للمترجم أن يمرر المؤقتة التي تم إنشاؤها بواسطة operator+ وتمرير ذلك إلى doit() في حين أن.

نصائح أخرى

هنا مثال:

#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(); تستفيد بشكل كبير من نسخ elision. إذا لم يُسمح بنسخ Elision ، فسيتطلب هذا الخط 3 دعوات من مُنشئ النسخ ودعوة مرتبطة بمدمار. مع السماح بالنسخ ، يمكن تقليله إلى 1 مكالمة من مُنشئ النسخ ، والآخر داخل BigCount::next() أين newcounter تم إعلانه.

لو operator = تم إعلانه وتعريفه على هذا النحو:

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

كان من المفترض أن يكون هناك invocations من مُنشئ النسخ ، حتى مع وجود نسخ. واحد لبناء newcounter والآخر لبناء tmp. وبدون نسخ ، لا يزال هناك 3. لهذا السبب الإعلان operator = لذلك تتطلب الوسيطة الخاصة بها استدعاء بناء النسخ يمكن أن يكون تحسينًا عند استخدام المصطلح "النسخ والمبادلة" لمشغل المهمة. عندما يتم استدعاء مُنشئ النسخ لإنشاء وسيطة ، قد يتم احتجازها ، ولكن إذا تم استدعاءها لإنشاء متغير محلي ، فقد لا يكون ذلك كذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top