سؤال

أحاول إنشاء فئة متجه "متفرق" في C ++ ، مثل ذلك:

template<typename V, V Default>
class SparseVector {
    ...
}

داخليًا ، سيتم تمثيله بواسطة std::map<int, V> (أين V هو نوع القيمة المخزنة). إذا لم يكن هناك عنصر في الخريطة ، فسوف ندعي أنه يساوي القيمة Default من حجة القالب.

ومع ذلك ، أواجه مشكلة في زيادة تحميل المشغل الفردي ، []. يجب أن أفرط في التحميل [] المشغل ، لأنني أقضي كائنات من هذه الفئة إلى وظيفة تعزيز تتوقع [] للعمل بشكل صحيح.

ال const الإصدار بسيط بما فيه الكفاية: تحقق مما إذا كان الفهرس موجود في الخريطة ، أو إرجاع قيمته إذا كان الأمر كذلك ، أو Default خلاف ذلك.

ومع ذلك ، فإن الإصدار غير المؤلف يتطلب مني إعادة مرجع ، وهذا هو المكان الذي أواجه فيه مشكلة. إذا كانت القيمة فقط قرأ, ، لا أحتاج (ولا أريد) إضافة أي شيء إلى الخريطة ؛ ولكن إذا كان الأمر كذلك مكتوبة, ، ربما أحتاج إلى وضع إدخال جديد في الخريطة. المشكلة هي أن الحمولة الزائدة [] لا يعرف ما إذا كانت القيمة قرأ أو مكتوبة. انها مجرد إرجاع مرجع.

هل يوجد أي طريقة لحل هذه المشكلة؟ أو ربما للعمل من حوله؟

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

المحلول

قد يكون هناك بعض الخدعة البسيطة للغاية ، ولكن على خلاف ذلك أعتقد operator[] لا بد من إرجاع شيء يمكن تعيينه من V (وتحويله إلى V) ، وليس بالضرورة V &. لذلك أعتقد أنك بحاجة إلى إعادة بعض الكائنات مع زيادة التحميل operator=(const V&), ، مما يخلق الإدخال في الحاوية المتفرقة.

سيتعين عليك التحقق من ما تفعله وظيفة Boost مع معلمة القالب الخاصة بها ، على الرغم من أنه-يؤثر التحويل المعرفة من قبل المستخدم إلى V على ما يمكن أن يكون سلاسل التحويل ممكنًا ، على سبيل المثال عن طريق منع وجود المزيد من التحويلات المعرفة من قبل المستخدم في نفس السلسلة.

نصائح أخرى

لا تدع المشغل والتنفيذ غير المشترك يعيد مرجعًا ، ولكن كائن وكيل. يمكنك بعد ذلك تنفيذ مشغل المهمة لكائن الوكيل لتمييز وصول القراءة إلى المشغل [] من الوصول إلى الكتابة.

إليك بعض الرسم الرمز لتوضيح الفكرة. هذا النهج ليس جميلًا ، لكن حسنًا - هذا C ++. لا يضيع المبرمجون C ++ الوقت في المنافسة في مسابقات التجميل (لن يكونوا فرصة أيضًا). ؛-)

template <typename V, V Default>
ProxyObject SparseVector::operator[]( int i ) {
   // At this point, we don't know whether operator[] was called, so we return
   // a proxy object and defer the decision until later
   return ProxyObject<V, Default>( this, i );
}

template <typename V, V Default>
class ProxyObject {
    ProxyObject( SparseVector<V, Default> *v, int idx );
    ProxyObject<V, Default> &operator=( const V &v ) {
      // If we get here, we know that operator[] was called to perform a write access,
      // so we can insert an item in the vector if needed
    }

    operator V() {
      // If we get here, we know that operator[] was called to perform a read access,
      // so we can simply return the existing object
    }
};

أتساءل ما إذا كان هذا التصميم سليمًا.

إذا كنت ترغب في إرجاع مرجع ، فهذا يعني أن عملاء الفصل يمكنهم تخزين نتيجة الاتصال operator[] في إشارة ، وقراءة من/اكتب إليها في أي وقت لاحق. إذا لم تقم بإرجاع مرجع ، و/أو لم تقم بإدخال عنصر في كل مرة يتم فيها معالجة فهرس معين ، فكيف يمكنهم القيام بذلك؟ (أيضًا ، لدي شعور بأن المعيار يتطلب حاوية STL مناسبة operator[] لإرجاع هذا المشغل مرجعًا ، لكنني لست متأكدًا من ذلك.)

قد تكون قادرًا على التحايل على ذلك من خلال إعطاء وكيلك أيضًا operator V&() (والتي من شأنها أن تنشئ الإدخال وتعيين القيمة الافتراضية) ، لكنني لست متأكدًا من أن هذا لن يفتح فقط فتحة حلقة أخرى في بعض الحالات التي لم أفكر فيها بعد.

std::map يحل هذه المشكلة عن طريق تحديد أن الإصدار غير المشغل من هذا المشغل يدرج دائمًا عنصرًا (ولا يقدم const الإصدار على الإطلاق).

بالطبع ، يمكنك دائمًا أن تقول هذا ليس حاوية STL خارج الجرف ، و operator[] لا يعيد المراجع العادية التي يمكن للمستخدمين تخزينها. وربما هذا جيد. انى اتسائل فقط.

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